This is a follow-up to Access sidecar files in a Mac sandboxed app.
Though not covered in the answer there, the Apple docs teach us that to access a \"related file\" we
No, because the OS will [potentially] actually copy the file to a different location in order to provide you with access to it, so you must use NSFileCoordinator
.
But all is not lost! There is a hack: even if your back-end code is designed to be portable, if you set the file-reading .cpp
to be "Objective-C++ Source" in Xcode, you can use Foundation features (#import <Foundation/Foundation.h>
) right there.
So wherever you currently instantiate and read-from an std::ifstream
, have an #if defined(PLATFORM_MAC_OS_X)
(or whatever) and, inside that, wrap your file-reading with the NSFileCoordinator
code.
Up top:
#ifdef PLATFORM_MAC_OS_X
#import <Foundation/Foundation.h>
@interface SidecarPresenter : NSObject<NSFilePresenter>
@property(readwrite, copy) NSURL* presentedItemURL;
@property(readwrite, copy) NSURL* primaryPresentedItemURL;
@property(readwrite, assign) NSOperationQueue* presentedItemOperationQueue;
-(instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt;
@end
@implementation SidecarPresenter
- (instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt
{
self = [super init];
if (self)
{
[self setPrimaryPresentedItemURL:imageURL];
[self setPresentedItemURL:[[imageUrl URLByDeletingPathExtension] URLByAppendingPathExtension:newExt]];
[self setPresentedItemOperationQueue:[NSOperationQueue mainQueue]];
}
return self;
}
- (void)dealloc
{
[_primaryPresentedItemURL release];
[_presentedItemURL release];
[super dealloc];
}
@end
#endif
And later:
#ifdef PLATFORM_MAC_OS_X
SidecarPresenter* presenter = [SidecarPresenter alloc];
[presenter initWithImageUrl:[NSURL fileURLWithPath:documentFilename]
andSidecarExtension:sidecarExtension]];
[presenter autorelease];
[NSFileCoordinator addFilePresenter:presenter];
NSFileCoordinator* coordinator = [[[NSFileCoordinator alloc] initWithFilePresenter:presenter] autorelease];
NSError* error = nil;
[coordinator coordinateReadingItemAtURL:presenter.presentedItemURL
options:NSFileCoordinatorReadingWithoutChanges
error:&error
byAccessor:^(NSURL* newURL)
{
std::ifstream strm([newURL fileSystemRepresentation]);
foo(strm);
}];
[NSFileCoordinator removeFilePresenter:presenter];
#else
std::ifstream strm(documentFilename);
foo(strm);
#endif
In this way, there's no ping-ponging back and forth between the back- and front-end. And the lambda is invoked synchronously so you don't have to worry about race conditions, either (just a bit of extra latency, potentially). The only cost is a bit of platform-specific leakage but at least it's hidden away inside a preprocessor directive.