简体   繁体   中英

FinderSync Extension - requestBadgeIdentifierForURL is never called

I have tested the template provided in Xcode for making a FinderSync Extension. Everything works well except two things:

a) The method requestBadgeIdentifierForURL is never called by the system when a folder is monitored so that badges are not set. What is going wrong here? I am right assuming that this method should be called when I eg move or scroll a monitored folder in Finder? By the way the methods beginObservingDirectoryAtURL and endObservingDirectoryAtURL are called properly in this context.

#import "FinderSync.h"

@interface FinderSync ()

@property NSURL *myFolderURL;

@end

@implementation FinderSync

- (instancetype)init {
    self = [super init];

    NSLog(@"%s launched from %@ ; compiled at %s", __PRETTY_FUNCTION__, [[NSBundle mainBundle] bundlePath], __TIME__);

    // Set up the directory we are syncing.
    self.myFolderURL = [NSURL fileURLWithPath:@"/Users/hmaass/Downloads"];
    [FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:self.myFolderURL];

    // Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
    [[FIFinderSyncController defaultController] setBadgeImage:[NSImage imageNamed: NSImageNameColorPanel] label:@"Status One" forBadgeIdentifier:@"One"];
    [[FIFinderSyncController defaultController] setBadgeImage:[NSImage imageNamed: NSImageNameCaution] label:@"Status Two" forBadgeIdentifier:@"Two"];

    return self;
}

#pragma mark - Primary Finder Sync protocol methods

- (void)beginObservingDirectoryAtURL:(NSURL *)url {
    // The user is now seeing the container's contents.
    // If they see it in more than one view at a time, we're only told once.
    NSLog(@"beginObservingDirectoryAtURL:%@", url.filePathURL);
}


- (void)endObservingDirectoryAtURL:(NSURL *)url {
    // The user is no longer seeing the container's contents.
    NSLog(@"endObservingDirectoryAtURL:%@", url.filePathURL);
}

- (void)requestBadgeIdentifierForURL:(NSURL *)url {
    NSLog(@"requestBadgeIdentifierForURL:%@", url.filePathURL);

    // For demonstration purposes, this picks one of our two badges, or no badge at all, based on the filename.
    NSInteger whichBadge = [url.filePathURL hash] % 3;
    NSString* badgeIdentifier = @[@"", @"One", @"Two"][whichBadge];
    [[FIFinderSyncController defaultController] setBadgeIdentifier:badgeIdentifier forURL:url];
}

#pragma mark - Menu and toolbar item support

- (NSString *)toolbarItemName {
    return @"testfifi";
}

- (NSString *)toolbarItemToolTip {
    return @"testfifi: Click the toolbar item for a menu.";
}

- (NSImage *)toolbarItemImage {
    return [NSImage imageNamed:NSImageNameCaution];
}

- (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu {
    // Produce a menu for the extension.
    NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
    [menu addItemWithTitle:@"Example Menu Item" action:@selector(sampleAction:) keyEquivalent:@""];

    return menu;
}

- (IBAction)sampleAction:(id)sender {
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];

    NSLog(@"sampleAction: menu item: %@, target = %@, items = ", [sender title], [target filePathURL]);
    [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"    %@", [obj filePathURL]);
    }];
}

@end

b) I get the following message in the log console of Xcode when running the template above:

2015-08-25 15:33:00.300 testfifi[855:8134] Failed to connect (colorGridView) outlet from (NSApplication) to (NSColorPickerGridView): missing setter or instance variable 2015-08-25 15:33:00.300 testfifi[855:8134] Failed to connect (view) outlet from (NSApplication) to (NSColorPickerGridView): missing setter or instance variable 2015-08-25 15:33:00.321 testfifi[855:8134] -[FinderSync init] launched from /Users/hmaass/Library/Developer/Xcode/DerivedData/testtest-egudnxkifjxirpbrjkohnatmjuro/Build/Products/Debug/testtest.app/Contents/PlugIns/testfifi.appex ; compiled at 20:38:18

Can someone help me to get rid of this message?

Thanks!

I already commented on your question but figured I should post a more complete answer.

It sounds like the issue you're having is another Finder Sync extension is "greedily" observing all folders, most likely the Dropbox Finder Integration. Try disabling all other Finder Sync extensions (under System Preferences -> Extensions -> Finder) and re-run your test.

If this resolves the issue, the problem is that Dropbox (or another app) has already called beginObservingDirectoryAtURL for the folder you're trying to monitor. Unfortunately, Apple's API is lacking in that there is no intelligent logic to who gets to monitor a folder when there are conflicting extensions. Currently, whichever Finder Sync extension starts first will "win".

Dropbox greedily monitors all folders under the user's home directory. I've written to both Apple and Dropbox to address this, but haven't heard any response. Currently, the (ugly) workaround I've implemented is to shutdown known "greedy" extensions, start my own extension, then restart the greedy extension.

Here's the workaround sample code for disabling "greedy" Finder Sync extensions. Nothing fancy, but it works.

(Adding this as a separate answer since it's really just a workaround and isn't necessarily the "correct" answer).

public static void main(String[] args) throws Exception {
    String[] greedyFSProcessNames =
        new String[] { "com.getdropbox.dropbox.garcon" };

    List<String> disabledGreedyFSProcessNames = new ArrayList<>();

    for (String greedyFSProcessName : greedyFSProcessNames) {
        if (!_isFSProcessRunning(greedyFSProcessName)) {
            continue;
        }

        _enableFSProcess(greedyFSProcessName, false);

        disabledGreedyFSProcessNames.add(greedyFSProcessName);
    }

    _enableFSProcess("com.dejuknow.myfindersync", true);

    for (String disabledGreedyFSProcessName :
        disabledGreedyFSProcessNames) {

        _enableFSProcess(disabledGreedyFSProcessName, true);
    }
}

private static boolean _isFSProcessRunning(String processName)
    throws Exception {

    BufferedReader bufferedReader = null;

    try {
        Process process = Runtime.getRuntime().exec(
            "pluginkit -m -i" + processName);

        bufferedReader = new BufferedReader(
            new InputStreamReader(process.getInputStream()));

        String line = null;

        while ((line = bufferedReader.readLine()) != null) {
            if (line.startsWith("+")) {
                return true;
            }
            else {
                return false;
            }
        }
    }
    finally {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
    }

    return false;
}

private static void _enableFSProcess(String processName, boolean enable)
    throws Exception {

    String electionArgument = null;

    if (enable) {
        electionArgument = "use";
    }
    else {
        electionArgument = "ignore";
    }

    String[] arguments = new String[] {
        "pluginkit", "-e", electionArgument, "-i", processName
    };

    while (_isFSProcessRunning(processName) != enable) {
        Process process = Runtime.getRuntime().exec(arguments);

        process.waitFor();

        Thread.sleep(100);
    }
}

The folders I know Dropbox uses: ~/Dropbox, ~/Documents and ~/Desktop. I have a FinderSync app too and I can display badges on all of my folders except those. Fortunately the context menu does not seem to suffer any conflict, both extension's menu items are shown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM