Re: Programatically set file URL of Core Data Document?
Re: Programatically set file URL of Core Data Document?
- Subject: Re: Programatically set file URL of Core Data Document?
- From: Jerry Krinock <email@hidden>
- Date: Tue, 19 May 2009 16:41:23 -0700
Thanks, Ben. With the background you provided, after a couple hours
of trial and error I finally devised a method that seems to work,
shown below in -[NSPersistentDocument saveMoveToNewPath:error_p:].
Indeed, it is necessary to jump through quite a few hoops, and to do
so in just the correct order.
On 2009 May 18, at 23:35, Ben Trumbull wrote:
Probably worth a bug.
OK, Bug ID# 6904434, "Difficult to move a Core Data document file".
Enhancement.
Jerry
The following categories may be added to a Core Data document-based
application, providing action methods for "New Document with
Wizard..." and "Save As Move..." items in the File menu.
********* NSPersistentDocument+Pathify.h **********
#import <Cocoa/Cocoa.h>
@interface NSPersistentDocument (Pathify)
/*!
@brief Action method for a "Save As Move..."
item in the "File" menu.
@details Presents a dialog which allows the user to
move the receiver's file to a new path, deleting the
old file.
Suggested tooltip for the "Save As Move..." menu item:
"This is like 'Save As...', except your document file will
also be removed from its current name/location."
*/
- (IBAction)saveAsMove:(id)sender ;
/*!
@brief Saves the receiver's file to a new path,
deleting the old file.
@param error_p Pointer to an NSError which, if not
NULL and the method fails, will point to an NSError
explaining the failure. Pass NULL if you are
not interested in the NSError.
@result YES if the method succeeds, NO if it fails.
*/
- (BOOL)saveMoveToNewPath:(NSString*)newPath
error_p:(NSError**)error_p ;
********* NSPersistentDocument+Pathify.m **********
#import "NSPersistentDocument+Pathify.h"
#import "NSDocumentController+FileExtensions.h"
@implementation NSPersistentDocument (Pathify)
- (BOOL)saveMoveToNewPath:(NSString*)newPath
error_p:(NSError**)error_p {
BOOL ok = YES ;
NSError* error_ ;
NSInteger errorCode = 157160 ;
// In case this comes from a dialog, make sure that newPath has the
// proper filename extension.
NSString* requiredExtension = [[NSDocumentController
sharedDocumentController] defaultFilenameExtension] ;
NSURL* newURL = [NSURL fileURLWithPath:newPath] ;
if (![[newPath pathExtension] isEqualToString:requiredExtension]) {
newPath = [newPath
stringByAppendingPathExtension:requiredExtension] ;
}
newURL = [NSURL fileURLWithPath:newPath] ;
// Core Data needs a document file on disk to start with ...
NSURL* oldURL = [self fileURL] ;
if (!oldURL) {
// This will execute for new, never-saved documents
NSString* oldPath = NSTemporaryDirectory() ;
NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier] ;
oldPath = [oldPath stringByAppendingPathComponent:bundleID] ;
oldPath = [oldPath stringByAppendingString:@"_temp"] ;
oldURL = [NSURL fileURLWithPath:oldPath] ;
[self setFileURL:oldURL] ;
}
NSString* oldPath = [[self fileURL] path] ;
// Core Data also needs a store ...
if (ok) {
NSManagedObjectContext* moc = [self managedObjectContext] ;
NSPersistentStoreCoordinator* psc = [moc
persistentStoreCoordinator] ;
NSArray* stores = [psc persistentStores] ;
if ([stores count] < 1) {
// This will execute for new, never-saved documents
NSPersistentStore* oldStore = [psc
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:oldURL
options:0
error:&error_] ;
ok = (oldStore != nil) ;
}
}
if (!ok) {
errorCode = 157161 ;
goto end ;
}
ok = [self saveToURL:oldURL
ofType:[self fileType]
forSaveOperation:NSSaveOperation
error:&error_] ;
if (!ok) {
errorCode = 157162 ;
goto end ;
}
// Needed if using the SQLite or other nonatomic store ...
NSFileManager* fileManager = [NSFileManager defaultManager] ;
ok = [fileManager moveItemAtPath:oldPath
toPath:newPath
error:&error_] ;
if (!ok) {
errorCode = 157163 ;
goto end ;
}
// Needed for NSDocument to use the new location for
// future saves, window title bar, etc. ...
[self setFileURL:newURL] ;
// Needed to avoid NSDocument displaying a sheet which tells the
// user that the document has been moved, and ask do they really
// want to save it in the new location, the next time they click
// in the menu File > Save ...
ok = [self saveToURL:newURL
ofType:[self fileType]
forSaveOperation:NSSaveOperation
error:&error_] ;
if (!ok) {
errorCode = 157164 ;
goto end ;
}
end:;
if (error_p && error_) {
NSString* errorDescription = [NSString stringWithFormat:
@"Error in %s",
__PRETTY_FUNCTION__] ;
NSDictionary* userInfo = [NSDictionary
dictionaryWithObjectsAndKeys:
errorDescription,
NSLocalizedDescriptionKey,
error_, NSUnderlyingErrorKey,
newPath, @"New Path",
oldURL, @"Old URL",
nil] ;
*error_p = [NSError errorWithDomain:[[NSBundle mainBundle]
bundleIdentifier]
code:errorCode
userInfo:userInfo] ;
}
return ok ;
}
- (void)saveMovePanelDidEnd:(NSSavePanel *)sheet
returnCode:(int)returnCode
contextInfo:(void*)contextInfo {
if (returnCode == NSFileHandlingPanelOKButton) {
NSURL* newURL = [sheet URL] ;
NSString* newPath = [newURL path] ;
NSError* error_ ;
BOOL ok = [self saveMoveToNewPath:newPath
error_p:&error_] ;
if (!ok) {
// In a real application, you will have customized
// error presentation using willPresentError: because
// Apple's is so lame -- doesn't even make it possible
// to recover the userInfo dictionary.
[self presentError:error_] ;
}
}
}
- (IBAction)saveAsMove:(id)sender {
NSSavePanel* panel ;
panel = [NSSavePanel savePanel] ;
SEL selector ;
NSString* message ;
selector = @selector(localize:) ;
if ([NSString respondsToSelector:selector]) {
// Category NSString (LocalizeSSY) is available
message = [NSString performSelector:selector
withObject:@"saveMoveDetail"] ;
}
else {
message = @"LOCALIZE: Choose a new name and/or location for
this document." ;
}
[panel setMessage:message] ;
[panel setCanCreateDirectories:YES] ;
NSDocumentController* dc = [NSDocumentController
sharedDocumentController] ;
selector = @selector(nextDefaultDocumentUrl) ;
NSURL* suggestedURL = nil ;
if ([dc respondsToSelector:selector]) {
suggestedURL = [dc performSelector:selector] ;
}
NSWindow* window = nil ;
NSArray* windowControllers = [self windowControllers] ;
if ([windowControllers count] > 0) {
window = [[windowControllers objectAtIndex:0] window] ;
}
[panel beginSheetForDirectory:nil
file:[[suggestedURL path]
lastPathComponent]
modalForWindow:window
modalDelegate:self
didEndSelector:@selector(saveMovePanelDidEnd:returnCode:contextInfo:)
contextInfo:NULL] ;
}
@end
********* NSObject+NewDocWizardDemo.h **********
#import <Cocoa/Cocoa.h>
@interface NSObject (NewDocWizardDemo)
/*!
@brief Action method for a "Save to Demo Path"
item in the "File" menu in demo applications.
@details Demonstrates how a Core Data document may be
created by a New Document Wizard and immediately saved to
a programmatically-determined path.
*/
- (IBAction)newDocumentWithWizardDemo:(id)sender ;
@end
********* NSObject+NewDocWizardDemo.m **********
#import "NSObject+NewDocWizardDemo.h"
#import "NSPersistentDocument+Pathify.h"
@implementation NSObject (NewDocWizardDemo)
- (NSString*)demoPath {
NSString* path = NSHomeDirectory() ;
path = [path stringByAppendingPathComponent:@"Desktop"] ;
NSString* filename = [NSString stringWithFormat:
@"WizDoc%0.0f",
[[NSDate date]
timeIntervalSinceReferenceDate]] ;
path = [path stringByAppendingPathComponent:filename] ;
return path ;
}
- (IBAction)newDocumentWithWizardDemo:(id)sender {
NSError* error ;
NSPersistentDocument* document = [[NSDocumentController
sharedDocumentController] openUntitledDocumentAndDisplay:YES
error
:&error] ;
// Insert code at this point to determine a path for the
// new document...
NSString* path = [self demoPath] ;
// Move the new document to the new path
BOOL ok = [document saveMoveToNewPath:path
error_p:&error] ;
if (!ok) {
// In a real application, you will have customized
// error presentation using willPresentError: because
// Apple's is so lame -- doesn't even make it possible
// to recover the userInfo dictionary.
[[NSDocumentController sharedDocumentController]
presentError:error] ;
}
}
@end
********* NSDocumentController+FileExtensions.h **********
#import <Cocoa/Cocoa.h>
@interface NSDocumentController (FileExtensions)
/*!
@brief Returns the first file extension listed in Info.plist for
a given
document-type.
@details In this context, document-type.Name refers to the
attribute shown
as the "Name" column in Xcode's Target inspector > Properties, and
is also
what is returned by, for example,
[[NSDocumentController sharedDocumentController] defaultType].
*/
+ (NSString*)fileExtensionForDocumentType:(NSString*)docType ;
/*!
@brief Returns the filename extension for the receiver's default
document type; i.e. its -defaultType.
*/
- (NSString*)defaultFilenameExtension ;
@end
********* NSDocumentController+FileExtensions.m **********
#import "NSDocumentController+FileExtensions.h"
@implementation NSDocumentController (FileExtensions)
+ (NSString*)fileExtensionForDocumentType:(NSString*)docType {
NSArray* docTypeDics = [[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleDocumentTypes"] ;
NSString* extension = nil ;
for (NSDictionary* docDic in docTypeDics) {
NSString* aDocType = [docDic
objectForKey:@"CFBundleTypeName"] ;
if ([aDocType isEqualToString:docType]) {
NSArray* extensions = [docDic
objectForKey:@"CFBundleTypeExtensions"] ;
if ([extensions count] > 0) {
extension = [extensions objectAtIndex:0] ;
}
break ;
}
}
return extension ;
}
- (NSString*)defaultFilenameExtension {
NSString* docType = [self defaultType] ;
return [NSDocumentController
fileExtensionForDocumentType:docType] ;
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden