Default Save location of document in Cocoa document based application - cocoa

I have created a Cocoa document based picture drawing application. I want that the default location of a new document created using my app in Save/Save As dialog should be in ~/Pictures/MyAppName/ directory.
How can I achieve this?
I tried more or less what Ole suggested below, but it doesn't work. Here is my implementation of prepareSavePanel. What am I doing wrong?
- (BOOL)prepareSavePanel:(NSSavePanel *)savePanel
{
if ([self fileURL] == nil) {
//new, not saved yet
[savePanel setExtensionHidden:NO];
//set default save location
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES);
if ([paths count] > 0) {
NSString *userPicturesPath = [paths objectAtIndex:0];
NSString *myDirPath = [userPicturesPath stringByAppendingPathComponent:#"MyAppName"];
//create directory is it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir;
BOOL useMyAppDir = NO;
if([fileManager fileExistsAtPath:myDirPath isDirectory:&isDir]){
if (isDir) {
useMyAppDir = YES;
}
} else {
//create the directory
if([fileManager createDirectoryAtPath:myDirPath withIntermediateDirectories:YES attributes:nil error:nil]){
useMyAppDir = YES;
}
}
if (useMyAppDir) {
NSURL * myAppDirectoryURL = [NSURL URLWithString:myDirPath];
[savePanel setDirectoryURL:myAppDirectoryURL];
}
}
} else {
[savePanel setExtensionHidden:[self fileNameExtensionWasHiddenInLastRunSavePanel]];
}
return YES;
}

In your NSDocument subclass, override -prepareSavePanel:
- (BOOL) prepareSavePanel:(NSSavePanel *)savePanel
{
// Set default folder if no default preference is present
NSDictionary *userDefaults = [[NSUserDefaults standardUserDefaults] persistentDomainForName:[[NSBundle mainBundle] bundleIdentifier]];
if ([userDefaults objectForKey:#"NSNavLastRootDirectory"] == nil) {
NSArray *picturesFolderURLs = [[NSFileManager defaultManager] URLsForDirectory:NSPicturesDirectory inDomains:NSUserDomainMask];
if ([picturesFolderURLs count] > 0) {
NSURL *picturesFolderURL = [[picturesFolderURLs objectAtIndex:0] URLByAppendingPathComponent:#"MyAppName"];
[savePanel setDirectoryURL:picturesFolderURL];
}
}
return YES;
}

Related

absoluteString "handleOpenURL " to match email attachment name?

I have a working code, but now find the need to import & save two different Sqlite files via email "Open in" option, I tried using UIAlertView to create to different save file paths but it only seems to work with one way
- (void)handleOpenURL:(NSURL *)urlx {
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dbimport forKey:#"import"];
[defaults synchronize];
NSLog(#"import saved");
databaseName = #"svrStoreData2.sql";
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathx = [documentsPath stringByAppendingPathComponent:databaseName]; [dbimport writeToFile:filePathx atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
}
Both file have specific file names so was wonder if I could use "if equal to string" & use an if statement after I have synchronised NSUserDefaults to give two file path save options
Thanks for any help in advance
This was my UIALertView try
UIAlertView *alertdata = [[UIAlertView alloc]
initWithTitle:#"What type of data do you want to import?"
message:#"Choose data type"
delegate:self
cancelButtonTitle:#"Import RM Name Data"
otherButtonTitles:#"Import Store Visit Data", nil];
NSInteger *buttonIndex = NULL;
NSLog(#"Button Index =%ld",(long)buttonIndex);
if (buttonIndex == 0)
{
}
else if (buttonIndex == 1)
{
databaseExportName = #"export.sql";
NSArray *pathssqlite3 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *pathssqliteDir2 = [pathssqlite3 objectAtIndex:0];
filePathsqlite3 = [pathssqliteDir2 stringByAppendingPathComponent:databaseExportName];
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSMutableData *dbimport = [[NSMutableData alloc] initWithContentsOfURL:urlx];
NSError *writeError = nil;
[dbimport writeToFile:filePathsqlite3 atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
} } [alertdata show];
Still trying to resolve, was hoping this would work, but still cannot crack it
`- (void)handleOpenURL:(NSURL *)urlx {
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dbimport forKey:#"import"];
[defaults synchronize];
NSLog(#"import saved");
if([[urlx absoluteString] isEqualToString:#"svrStoreData2.sql"])
{
NSLog(#"If statement called");
//
databaseName = #"svrStoreData2.sql";
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathx = [documentsPath stringByAppendingPathComponent:databaseName];
[dbimport writeToFile:filePathx atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
//
else {
databaseExportName = #"export.sql";
NSString *documentsPathy = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathy = [documentsPathy stringByAppendingPathComponent:databaseExportName];
[dbimport writeToFile:filePathy atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
NSLog(#"If statement Not called");
}
}
}
`
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex(NSInteger)buttonIndex {
if ([alertView tag] == 14)
{
if (buttonIndex == 1)
{
[self reviewSave];
}
}
if ([alertView tag] == 11) {
if (buttonIndex == 1)
{
NSDictionary *defaultsDictionary = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
for (NSString *key in [defaultsDictionary allKeys]) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];}
}
}
Solved the problem, I had another UIAlert, so by adding Tags sorted it

How to make an OSX app use a sqlite database in the bundle?

I have an app that is intended to ship with a pre-populated sqlite database. I will include that database on the bundle. How do I modify the delegate to use that database instead of generating a new one?
This is my first serious app on OSX.
The current methods on the delegate is like this right now:
// Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.addfone.LoteriaMac" in the user's Application Support directory.
- (NSURL *)applicationFilesDirectory
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
return [appSupportURL URLByAppendingPathComponent:#"com.myApp.BotMax"];
}
// Creates if necessary and returns the managed object model for the application.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"BotMax" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
// Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:#[NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
} else {
if (![properties[NSURLIsDirectoryKey] boolValue]) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"BotMax.storedata"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_persistentStoreCoordinator = coordinator;
return _persistentStoreCoordinator;
}
The database file is named like BotMax.sqlite
thanks.
The simplest way to ship a pre-filled SQLite DB would be to copy the .sqlite file into the Application Support folder before the persistentStoreCoordinator is initialised.
The below code just copies the bundled DB. There are some things to consider before using that approach:
This simple approach only copies the initial DB once.
It does not consider merging or migrating existing stores (e.g when you ship an update with a changed model)
Core Data is using a new journal_mode since iOS 7 & OS X 10.9. This mode creates an additional file when saving the DB. (*.sqlite-wal). If the program that creates your pre-filled SQLite file was linked against OS X 10.9/iOS 7 or later SDKs, you'll also have to ship the .sqlite-wal file.
Alternatively, you can disable the WAL journal mode in the app that creates your pre-filled DB by passing #{ NSSQLitePragmasOption : #{ #"journal_mode" : #"DELETE" } }; as option dict to addPersistentStoreWithType
The following code is based on the standard Xcode template for non-document based Core Data apps:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSString* dbFilename = #"BotMax.sqlite";
NSURL* initialDBURL = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:dbFilename];
NSURL* workingDBURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:dbFilename];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* applicationFilesDirectory = [self applicationFilesDirectory];
NSError* error = nil;
if(![fileManager fileExistsAtPath:[workingDBURL path]])
{
BOOL result = [fileManager copyItemAtURL:initialDBURL toURL:workingDBURL error:&error];
if(!result)
{
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
...
}
objc.io issue #4 has a section about shipping pre-filled Core Data stores: http://www.objc.io/issue-4/importing-large-data-sets-into-core-data.html

NSPersistentStoreCoordinator database storage location applicationSupportFolder issue

Right now my App is placing the database in the ~/Library folder but I would like it to place it in a more organized manner ~/Library/App Name but I can not figure out how to do that with this block of code.
(NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSFileManager *fileManager;
NSString *applicationSupportFolder = nil;
NSString *dataFilePath;
NSURL *url;
NSError *error;
fileManager = [NSFileManager defaultManager];
applicationSupportFolder = [self applicationSupportFolder];
if ( ![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL] ) {
[fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil];
}
dataFilePath = [applicationSupportFolder stringByAppendingPathComponent: #"sample.dat"];
if( NO == [[ NSFileManager defaultManager ] fileExistsAtPath: dataFilePath ] )
{
[[ NSFileManager defaultManager ] copyPath: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: #"sample.dat"]
toPath: dataFilePath handler: NULL ];
}
url = [NSURL fileURLWithPath:dataFilePath];
if( url )
{
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]){
[[NSApplication sharedApplication] presentError:error];
}
}
return persistentStoreCoordinator;
}
This code works it just places the database in a pretty terrible location IMO. I am new to Obj-C and just inherited this code set from a another developer.
UPDATE:
Adding the final working code below.
(NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSFileManager *fileManager;
NSString *applicationSupportFolder = nil;
NSString *dataFilePath;
NSURL *url;
NSError *error;
fileManager = [NSFileManager defaultManager];
applicationSupportFolder = [self applicationSupportFolder];
NSString *aappSupportFolder = [applicationSupportFolder stringByAppendingPathComponent: #"APP"];
if ( ![fileManager fileExistsAtPath:aappSupportFolder isDirectory:NULL] ) {
[fileManager createDirectoryAtPath:aappSupportFolder attributes:nil];
}
dataFilePath = [aappSupportFolder stringByAppendingPathComponent: #"sample.dat"];
if( NO == [[ NSFileManager defaultManager ] fileExistsAtPath: dataFilePath ] )
{
[[ NSFileManager defaultManager ] copyPath: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: #"sample.dat"]
toPath: dataFilePath handler: NULL ];
}
url = [NSURL fileURLWithPath:dataFilePath];
if( url )
{
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]){
[[NSApplication sharedApplication] presentError:error];
}
}
return persistentStoreCoordinator;
}
Well it is very simple just you have to create folder name APP inside library folder of your machine and then you have to move your app inside the same path . Please try below:-
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *pathString= [paths objectAtIndex:0];
NSError *err=nil;
//below is your application previous path which we will move to new path
NSString *dataFilePath = [pathString stringByAppendingPathComponent: #"sample.dat"];
NSString *applicationSupportFolder = [pathString stringByAppendingPathComponent: #"App"];
//Here we have created directory folder as App inside Library below
if ( ![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL] ) {
[fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil];
}
if ([fm moveItemAtPath:dataFilePath toPath:applicationSupportFolder error:&err])
{
NSLog(#"success");
}
else
{
NSLog(#"%#",[err localizedDescription]);
}

Using multiple imagepickers on one view

I have a working imagepicker that upon button click and hold gesture, allows the user to upload an image to the disk, and the user can change the image using the same gesture. The only issue is, however, I need this to be done twice on the same view (i.e. I have two imageviews, two buttons to change each of the imageviews, etc.), and I am stumped on how to get the second one to work. This is essentially what the view looks like:
Here is my current code:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//
// Saving into Documents folder
//
NSString* path = [NSHomeDirectory() stringByAppendingString:#"/Documents/first.png"];
BOOL ok = [[NSFileManager defaultManager] createFileAtPath:path
contents:nil attributes:nil];
if (!ok) {
NSLog(#"Error creating file %#", path);
} else {
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
[myFileHandle writeData:UIImagePNGRepresentation(info [UIImagePickerControllerOriginalImage])];
[myFileHandle closeFile];
}
//
// Loading from documents
//
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
UIImage* loadedImage = [UIImage imageWithData:[myFileHandle readDataToEndOfFile]];
self.chosenImage = loadedImage;
[self.imageView setImage:self.chosenImage];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)onLongTile:(UILongPressGestureRecognizer *)gesture
{
switch ([gesture state]) {
case UIGestureRecognizerStateBegan:{
NSString *actionSheetTitle = #"Photo Options"; //Action Sheet Title
NSString *type = #"Upload Photo";
NSString *cancelTitle = #"Cancel";
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:actionSheetTitle
delegate:self
cancelButtonTitle:cancelTitle
destructiveButtonTitle:nil
otherButtonTitles:type, nil];
[actionSheet showInView:self.view]; }
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *buttonTitle = [actionSheet buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:#"Photo"]) {
self.imagePicker = [[UIImagePickerController alloc] init];
self.imagePicker.delegate = self;
[self.imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
[self presentViewController:self.imagePicker animated:YES completion:nil];
}
}
Updated code with tags:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if (self.imageViewOne.tag == 100)
{
//
// Saving into Documents folder
//
NSString* path = [NSHomeDirectory() stringByAppendingString:#"/Documents/one.png"];
BOOL ok = [[NSFileManager defaultManager] createFileAtPath:path
contents:nil attributes:nil];
if (!ok) {
NSLog(#"Error creating file %#", path);
} else {
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
[myFileHandle writeData:UIImagePNGRepresentation(info [UIImagePickerControllerOriginalImage])];
[myFileHandle closeFile];
}
//
// Loading from documents
//
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
UIImage* loadedImage = [UIImage imageWithData:[myFileHandle readDataToEndOfFile]];
self.chosenImageOne = loadedImage;
[self.imageViewOne setImage:self.chosenImageOne];
[self dismissViewControllerAnimated:YES completion:nil];
}
if (self.imageViewTwo.tag == 200)
{
//
// Saving into Documents folder
//
NSString* path = [NSHomeDirectory() stringByAppendingString:#"/Documents/two.png"];
BOOL ok = [[NSFileManager defaultManager] createFileAtPath:path
contents:nil attributes:nil];
if (!ok) {
NSLog(#"Error creating file %#", path);
} else {
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
[myFileHandle writeData:UIImagePNGRepresentation(info [UIImagePickerControllerOriginalImage])];
[myFileHandle closeFile];
}
//
// Loading from documents
//
NSFileHandle* myFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
UIImage* loadedImage = [UIImage imageWithData:[myFileHandle readDataToEndOfFile]];
self.chosenImageTwo = loadedImage;
[self.imageViewTwo setImage:self.chosenImageTwo];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
From what I understood of the question, set tags to your UIImageView and change is accordingly. something like:
[self.imageViewOne setTag:100];
[self.imageViewTne setTag:200];
And then in your delegates you can check the tag value and do the needful:
if ( imageView.tag == 100)
{
// Code for first imageView
}

Automatic Lightwieight Migration for core data Problem

I'm having problem use Automatic Lightweight Migration Code in my App delegate !
I read all the apple's documentations about "Automatic Lightweight Migration" but after all I can't find my way to use the codes that is prepared to Automatic Lightweight Migration.
Recently, I just added some new Attribute to an Entity in my data model and I want to keep my old data.
my app delegate code is like this :
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (__persistentStoreCoordinator) {
return __persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
else {
if ([[properties objectForKey:NSURLIsDirectoryKey] boolValue] != YES) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"FinancingPro.storedata"];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
[__persistentStoreCoordinator release], __persistentStoreCoordinator = nil;
return nil;
}
return __persistentStoreCoordinator;
Now I don't know how to change this code to have Automatic Lightweight Migration !
Please Note that my db is NOT SQLlite.
You need to set the options dictionary with the NSInferMappingModelAutomaticallyOption key here:
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
By passing a nil value for options you are telling the store to ignore any migration.

Resources