How to set up initial Core Data store in a Mac app? - cocoa

I am an experienced iOS developer trying to make my first Mac app. I want to use Core data to store the data in my app. In my iOS apps, I generally have a pre-created SQLite file which is used as the initial state of the data store, and which is moved into place on the first time the app is run, like this:
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"Datafile.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"Datafile-DefaultData" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
I want to do something similar in the mac app, except put the data in the ~/Library/Application Support/MyApp directory. I can't seem to figure out how to do it. Any pointers?

In Xcode 4, the default Core Data project template, for some reason, uses the root ~/Library directory (ie ~/Library/Application to store your Core Data file. You should change this anyways (because it's a bad idea), but once you do that, it should work as you expect. I believe the default name on the Mac is storedata, and you should also note that you'll need to change the store type from XML, which is the default.

Related

iOS8: Sharing common files between apps

Two iOS: AppA and AppB
Both Apps are created by me and I would like to share one single file between both of the apps.
i.e. AppA launches on deviceA and the User saves data on fileA. Later the User launches AppB on the same (deviceA) and also saves data on fileA. Both apps are saving data on the same file.
I'm aware that I can use NSUserDefaults and share Keychain between apps, but that's not what I'm looking for.
I read up on document extensions provider and app groups, but I'm confused if I can use these for this scenario? Or is there any other way to accomplish this?
You can do it using Application Group Container Directory:
NSFileManager *fm = [NSFileManager defaultManager];
NSString *appGroupName = #"Z123456789.com.example.app-group"; /* For example */
NSURL *groupContainerURL = [fm containerURLForSecurityApplicationGroupIdentifier:appGroupName];
NSError* theError = nil;
if (![fm createDirectoryAtURL: groupContainerURL withIntermediateDirectories:YES attributes:nil error:&theError]) {
// Handle the error.
}
You could just upload the files after saving to your server and make both apps request updates for the file whenever they are launched.
Hope that helps :)

dismissGrantingAccessToURL of UIDocumentPickerExtensionViewController is not working

I am working on document provider extension for import mode.
URL of file is sitting inside the shared container shared by both extension & container app.
I got assertion failure saying that it should sit inside shared container/File Provider Storage directory.
So I copied file from original directory to File Provider Storage directory.
I have a file in original location & I got a file in new copied location also. I checked for data length of the file also, Its proper ,
But when I call [self dismissGrantingAccessToURL:toUrl];
Its not dismissing UIDocumentPickerExtensionViewController,
I am not facing any exceptions , but it wont dismiss and initiate the transfer, So user can still access the extension.
Below is my code, If anyone came across the same thing please leave your reply.
- (void)userChoosesEntityOfUrl:(NSURL *)url
{
NSURL *toUrl = [self.documentStorageURL URLByAppendingPathComponent:[url lastPathComponent]];
if ([[NSFileManager defaultManager] fileExistsAtPath:[toUrl path]]) {
[[NSFileManager defaultManager] removeItemAtPath:[toUrl path] error:nil];
}
if ([[NSFileManager defaultManager] copyItemAtURL:url toURL:toUrl error:nil]) {
NSLog(#"%#", [toUrl path]);
}
if (![[NSFileManager defaultManager] fileExistsAtPath:[toUrl path]]) {
NSLog(#"File Doesn't exists at this path");
return;
}
NSLog(#"Data Length %i",[[NSData dataWithContentsOfFile:[toUrl path]] length]);
[self dismissGrantingAccessToURL:toUrl];
}
When debugging your implementation of NSFileProviderExtension, you usually end up stopping/killing it using Xcode. After this, iOS often has problems restarting your file extension. This results in your document picker not being dismissed after calling dismissGrantingAccessToURL:. This bug also affects your subclass of UIDocumentPickerExtensionViewController which sometimes won't start (you only see the navigation bar of the document picker but not the content).
The workaround is to reboot your device.
This will occur if you have a backing File Provider extension that has not been fully implemented. Remove the File Provider target from your embedded extension phase, set your document extension only support import/export modes in the Info.plist, and do a product -> Clean before building and running your extension.

How to force NSArrayController to reload MOC contents to reflect latest fresh data

I am working on a Mac & iOS App, with iCloud CoreData in between to synchronize the data.
When I update some thing from iOS App, and the updates are already migrated to the PersistentStore in Mac App while the Mac App is running. The problem is I cannot find an effective way to force the NSArrayController to reload all data from the store.
tried -(void) fetch:(id)sender; only can see the delete or added entity, but the updated model property not refreshed...
Please help. Thanks
If you see the latest data in the managed object context, but not in the array controller, you want:
[_yourManagedObjectContext processPendingChanges];
[_yourArrayController fetchWithRequest:nil merge:YES error:&error];
[_yourArrayController rearrangeObjects];
I use this in a Mac/iOS iCloud app to update the Mac app's data when the iCloud store changes.
Following is the reply from Apple's Developer Technical support. It worked for me. Thanks all for providing solutions.
For the OS X app, when reloadFetchResults is called, you can ask the NSManagedObjectContext to reset itself and perform the fetch again.
- (void)reloadFetchedResults:(NSNotification *)note
{
NSDictionary *userInfoDict = [note userInfo];
NSManagedObjectContext *moc = self.coreDataController.mainThreadContext;
// this only works if you used NSMainQueueConcurrencyType
// otherwise use a dispatch_async back to the main thread yourself
//
[moc performBlock:^{
[self mergeiCloudChanges:userInfoDict forContext:moc];
[moc reset];
[self.tableArrayController fetch:self];
}];
}
I've found that
[self.managedObjectContext reset];
[myArrayController fetch:self];
forces my NSTableView (with NSArrayController) to re-populate and display newly processed NSManagedObjects.
in case someone else finds this...
in my case, I was building the array from user interaction (not Core Data), and then trying to show the tableView when they were done (on the same window)... and of course... seeing nothing!
[arrayController rearrangeObjects];
just before I wanted to show the tableView fixed it for me.

CoreData - Problem accessing database on another machine

I've got a problem with my CoreData, but I'm sure I'm doing something wrong conceptually.
I'm trying to access a CoreData sql file on one machine in my network from another machine. I'm trying to do this from a cluster-like application. Each machine has the same copy of the software and needs to point to the database on this one machine.
My model and context load fine for the machine that the database is on. The other machine, gives me error 13400 NSPersistentStoreInvalidTypeError
Here's the bit of code:
NSError *error = nil;
NSURL *mdlurl = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"OsiriXDB_DataModel" ofType:#"mom"]];
_model = [[NSManagedObjectModel alloc] initWithContentsOfURL: url];
NSURL *dburl = [NSURL URLWithString:[NSString stringWithUTF8String:_DBPath.c_str()]];
// The dburl has a format like: file://192.168.0.2/Users/slate/Documents/OsiriX%20Data/Database.sql which addresses the machine the data sits on.
_storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: _model];
_context = [[NSManagedObjectContext alloc] init];
[_context setPersistentStoreCoordinator: _storeCoordinator];
if (![_storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:dburl options:nil error:&error]) {
NSLog(#"Error loading store: %#", error); // Error Shows up Here
NSLog(#"MOM: %#",_model); // Model looks OK. Lots of print outs, with the correct names and stuff. (so technical).
}
I confess to not knowing a ton about CoreData. Is it because it's on a different machine? I read this online but I don't think that's my issue. If it is, I have no idea how to fix it because I can't find any .xml files in my ~/Library/Application\ Support/ directory relating to either MyApp or OsiriX which is the program that created the database.
Am I doing the wrong thing to load CoreData across a network?
If not, what should I be doing?
Thanks,
The error indicates that persistent store coordinator thinks that the file is not the proper format for a NSSQLiteStoreType. That suggest that the file was found. If it couldn't locate the file or access the directory you would get another error.
I'm not sure what you problem is specifically but I can tell you in general that Core Data is not intended as a concurrent database. It's not even really a database at all. It's actually a runtime object graph management system intended to manage an app's model layer with persistence tacked on the side as an option. There are no Core Data options for controlling multiple instances of an app simultaneously accessing the same store. You might be able to do so by setting the store as readonly but I don't know for sure.
It sounds like you need a real database running on your server.

Cocoa equivalent of .NET's Environment.SpecialFolder for saving preferences/settings?

How do I get the reference to a folder for storing per-user-per-application settings when writing an Objective-C Cocoa app in Xcode?
In .NET I would use the Environment.SpecialFolder enumeration:
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
What's the Cocoa equivalent?
In Mac OSX application preferences are stored automatically through NSUserDefaults, which saves them to a .plist file ~/Library/Preferences/. You shouldn't need to do anything with this file, NSUserDefaults will handle everything for you.
If you have a data file in a non-document based application (such as AddressBook.app), you should store it in ~/Library/Application Support/Your App Name/. There's no built-in method to find or create this folder, you'll need to do it yourself. Here's an example from one of my own applications, if you look at some of the Xcode project templates, you'll see a similar method.
+ (NSString *)applicationSupportFolder;
{
// Find this application's Application Support Folder, creating it if
// needed.
NSString *appName, *supportPath = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
if ( [paths count] > 0)
{
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
supportPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:appName];
if ( ![[NSFileManager defaultManager] fileExistsAtPath:supportPath] )
if ( ![[NSFileManager defaultManager] createDirectoryAtPath:supportPath attributes:nil] )
supportPath = nil;
}
return supportPath;
}
Keep in mind that if your app is popular you'll probably get requests to be able to have multiple library files for different users sharing the same account. If you want to support this, the convention is to prompt for a path to use when the application is started holding down the alt/option key.
For most stuff, you should just use the NSUserDefaults API which takes care of persisting settings on disk for you.

Resources