re: Programatically set file URL of Core Data Document?

2009-05-19 Thread Ben Trumbull

I'm trying to programatically set the initial file url (path,
filename) of an Core Data NSPersistentDocument, or if that is not
possible, at least populate the Save panel with an initial filename.


This is more an NSDocument / UI problem.  Pushing this down into the  
model layer will make Core Data unhappy, as you've discovered.


Do you just want to populate the initial Save dialog ?  You could  
probably just stash the desired URL as an ivar on your document  
subclass and customize the save dialog.



At first, I was using -[NSDocument
saveToURL:ofType:forSaveOperation:error:].  That worked fine until I
switched from XML to SQLite store, whereupon I began getting Could
not merge changes errors when saving [1].


It works for atomic stores because they've cached the entire file in  
memory, and will overwrite everything on each save.  For a store doing  
incremental operations like SQLite, this fails.  The original, or a  
copy, needs to exist at the destination.  Basically, the SQLite store  
is just fetching and saving deltas.  Applying deltas over an empty new  
file, or some random file, just doesn't work.



Then, I tried to implement -prepareSavePanel: and in there to
setFileURL: if it is found to be nil, as it is for a new document.
But this results in a nil is not a valid persistent store being
logged upon saving. [1]


The Core Data layer does not accept nil as a valid persistent URL.  We  
can't save or load stuff from nil.  Setting the URL for a persistent  
store only changes how we refer to the store.  The exact same original  
file MUST exist at the new URL location.  Basically setting the URL  
tells Core Data to update it's path because the file moved out from  
underneath us.



Then, I tried removing the old persistent store with
removePersistentStore:error: and adding a new one with
addPersistentStoreWithType:configuration:nilURL:options:error:.  But
that results upon saving in errors such as The NSManagedObject with
ID:0x16d22cb0 x-coredata://... has been invalidated.



If you remove a store, all the managed objects from that store are  
invalidated.  Yanking the store out of the stack makes everything from  
it poisoned fruit, so to speak.  Your code still has references to  
managed objects from the removed store, and when you use them they  
fail this way.



[1] In the error's userInfo, in the the 'conflictList', I see that the
newVersion is 0 but the old version is 1.  That seems kind of weird
but I'm just guessing...


The versions are always positive integers.  0 means it was deleted.   
The new version is non-existence.


What is the correct approach to do programatically set the file URL  
of  an NSPersistentDocument?


You need to actually have a document before you can change the URL.   
If you want to move the document, you can move the file and then call  
setURL.  If you want to make the default URL appear to be something  
particular for an unsaved document, I would recommend doing that as a  
UI artifact outside of Core Data.


Probably worth a bug.

- Ben

___

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


Re: Programatically set file URL of Core Data Document?

2009-05-19 Thread Jerry Krinock


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)

/*!
 @briefAction 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 ;

/*!
 @briefSaves the receiver's file to a new path,
 deleting the old file.

 @paramerror_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
   

Programatically set file URL of Core Data Document?

2009-05-18 Thread Jerry Krinock
I'm trying to programatically set the initial file url (path,  
filename) of an Core Data NSPersistentDocument, or if that is not  
possible, at least populate the Save panel with an initial filename.


At first, I was using -[NSDocument  
saveToURL:ofType:forSaveOperation:error:].  That worked fine until I  
switched from XML to SQLite store, whereupon I began getting Could  
not merge changes errors when saving [1].


Then, I tried to implement -prepareSavePanel: and in there to  
setFileURL: if it is found to be nil, as it is for a new document.   
But this results in a nil is not a valid persistent store being  
logged upon saving. [1]


Then, I tried using -[NSPersistentStoreCoordinator  
migratePersistentStore:toURL:options:withType:error:].  But this  
results in a crash upon saving, in -[NSDocument  
_saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo 
:]. [2]


Then, I tried removing the old persistent store with  
removePersistentStore:error: and adding a new one with  
addPersistentStoreWithType:configuration:nilURL:options:error:.  But  
that results upon saving in errors such as The NSManagedObject with  
ID:0x16d22cb0 x-coredata://... has been invalidated.


For brevity, I have omitted my arguments for and against each of the  
four approaches.


What is the correct approach to do programatically set the file URL of  
an NSPersistentDocument?  Or if someone could rule out one or more of  
my four approaches, at least I would know which to not waste any time  
beating on :)


Thank you,

Jerry Krinock


[1] In the error's userInfo, in the the 'conflictList', I see that the  
newVersion is 0 but the old version is 1.  That seems kind of weird  
but I'm just guessing...


conflictList:
(
  {
cachedRow = {
  configureAutomatically = 1;
  defaultSortable = 1;
  displaysFolders = 1;
  doExportAfterSave = null;
  doFindDupesAfterOpen = null;
  doImportAfterOpen = null;
  doOpenAfterLaunch = null;
  doSortAfterOpen = null;
  filterIgnoredPrefixes = 0;
  hasBar = 0;
  hasMenu = 0;
  hasOhared = 0;
  hasUnfiled = 0;
  ignoreDisparateDupes = 0;
  lastDupesDone = null;
  lastDupesMaybeMore = null;
  lastSortDone = null;
  lastSortMaybeNot = null;
  lastTouched = null;
  lastVerifyDone = null;
  lastVerifyMaybeNot = null;
  notes = Document created 2009 May 17 17:40 by jk.;
  rootLeavesOk = 0;
  rootNotchesOk = 0;
  rootSoftainersOk = 0;
  rootSortable = 127;
  satchAll1 = 1;
  satchAll2 = 1;
  softainersAnyRootIndex = 0;
  sortBy = 0;
  sortFoldersAt = 0;
  tagDelimiter = 61441;
  uuid = F1724F28-C8A1-4F92-9334-C1C48721868B;
  visitor = visitDB;
};
databaseRow = {
};
newVersion = 0;
object = Bookshig 0x2800630 owned by Bookshelf 0x165ecac0  
untitled_5.bkmslf;

oldVersion = 1;
  }
)

[2]  Actually, the crash occurs in the next call, -[NSDocument  
_willPresentSavingError:forOperation:url:].  I suppose that the 'url'  
parameter may be invalid.


 
___


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