Is there an alternative to NSFileCoordinator for opening related files in a sandbox?

后端 未结 1 991
半阙折子戏
半阙折子戏 2021-01-22 16:18

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

相关标签:
1条回答
  • 2021-01-22 17:05

    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.

    0 讨论(0)
提交回复
热议问题