re: Programatically set file URL of Core Data Document?
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?
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?
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