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.&nbsp;  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 (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to