NSFilePresenter methods are never called

I am trying to write a simple (toy) program that uses the NSFilePresenter and NSFileCoordinator methods to view a file for changes.

The program consists of a text view that loads a text file (hardcoded) and a button that saves the file with any changes. The idea is that I have two instances, and saving in one instance will force another instance to reload the modified file.

Loading and saving a file works fine, but NSFilePresenter methods are never called. All this is based on the FileManager class, which implements the NSFilePresenter protocol. The code is as follows:

Interface:

@interface FileManager : NSObject <NSFilePresenter> @property (unsafe_unretained) IBOutlet NSTextView *textView; - (void) saveFile; - (void) reloadFile; @end 

Implementation:

 @implementation FileManager { NSOperationQueue* queue; NSURL* fileURL; } - (id) init { self = [super init]; if (self) { self->queue = [NSOperationQueue new]; self->fileURL = [NSURL URLWithString:@"/Users/Jonathan/file.txt"]; [NSFileCoordinator addFilePresenter:self]; } return self; } - (NSURL*) presentedItemURL { NSLog(@"presentedItemURL"); return self->fileURL; } - (NSOperationQueue*) presentedItemOperationQueue { NSLog(@"presentedItemOperationQueue"); return self->queue; } - (void) saveFile { NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self]; NSError* error; [coordinator coordinateWritingItemAtURL:self->fileURL options:NSFileCoordinatorWritingForMerging error:&error byAccessor:^(NSURL* url) { NSString* content = [self.textView string]; [content writeToFile:[url path] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; }]; } - (void) reloadFile { NSFileManager* fileManager = [NSFileManager defaultManager]; NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self]; NSError* error; __block NSData* content; [coordinator coordinateReadingItemAtURL:self->fileURL options:0 error:&error byAccessor:^(NSURL* url) { if ([fileManager fileExistsAtPath:[url path]]) { content = [fileManager contentsAtPath:[url path]]; } }]; dispatch_async(dispatch_get_main_queue(), ^{ [self.textView setString:[[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]]; }); } // After this I implement *every* method in the NSFilePresenter protocol. Each one // simply logs its method name (so I can see it has been called) and calls reloadFile // (not the correct implementation for all of them I know, but good enough for now). @end 

Note. reloadFile is called in applicationDidFinishLaunching , and saveFile is called every time the save button is clicked (via the application delegate).

The only NSFilePresenter method that is ever called (goes through the logs) is isURUR (which is called four times when the program starts and downloads the file, and three times every time the "Save" button is clicked). When you click on save in the second instance, there is no noticeable effect in the first case.

Can someone tell me what I'm doing wrong here?

+4
source share
2 answers

I struggled with this exact problem for a long time. For me, the only method to be called was -presentedSubitemDidChangeAtURL: (I controlled the directory, not the file). I opened technical support with Apple, and their answer was that it was a mistake, and the only thing we can do right now is to do everything through -presentedSubitemDidChangeAtURL: if you follow the directory. Not sure what can be done while monitoring the file.

I would advise anyone who has encountered this problem to file an error message ( https://bugreport.apple.com ) to encourage Apple to fix this problem as soon as possible.

+3
source

(I understand this is an old question, but ... :))

First of all, I noticed that you do not have [NSFileCoordinator removeFilePresenter:self]; anywhere (it should be in dealloc ).

Secondly, you wrote:

  // After this I implement *every* method in the NSFilePresenter protocol. Each one // simply logs its method name (so I can see it has been called) and calls reloadFile // (not the correct implementation for all of them I know, but good enough for now). 

You are right: this is a wrong implementation! And you are mistaken: this is not good enough, because for methods like accommodatePresentedItemDeletionWithCompletionHandler: that take a completion block as a parameter, you need to actually name this completion block when you implement them, for example.

  - (void) savePresentedItemChangesWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler { // implement your save routine here, but only if you need to! if ( dataHasChanged ) [self save]; // <-- meta code // NSError * err = nil; // <-- = no error, in this simple implementation completionHandler(err); // <-- essential! } 

I don’t know if this is the reason why your protocol methods are not called, but this is definitely a place to start. Well, assuming that you have not yet developed what was wrong in the last three years !:-)

+1
source

All Articles