How to enable FinderSync Extension in macOS System Preferences - objective-c

I am integrating FinderSync Extension in my Cocoa Application to show badges in files and folders. Look at the below two scenario:
When i run application using FinderSync Extension (like DemoFinderSync) look at the blue popup in the below image, in that case Extension is added in the System Preference with Check mark and called that principal class "FinderSync.m" as well.
When i run application using my Application Scheme (like DemoApp) look at the blue popup in the below image, in that case Extension is added in the System Preference but without check mark and that principal class "FinderSync.m" do not call and FinderSync Extension does not work in this case.
Does anybody have an idea how to enable Finder Extension in the System Preference using second scenario?

Non-debug scheme (#if !DEBUG):
system("pluginkit -e use -i com.domain.my-finder-extension");
When running under debugger give path to your extension directly:
NSString *pluginPath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:#"My Finder Extension.appex"];
NSString *pluginkitString = [NSString stringWithFormat:#"pluginkit -e use -a \"%#\"", pluginPath];
system([pluginkitString cStringUsingEncoding:NSUTF8StringEncoding]);
Specify this in your applicationDidFinishLaunching method. You should also manually turn this on only once so that if user turned your extension off in the System Preferences you don't turn it on every time your application starts. I set an NSUserDefaults key the first time user launches my app that has the finder sync extension support.

I got the solution:
Code to Enable Extension (bundle ID)
system("pluginkit -e use -i YourAppBundleID")
Code to Disable Extension (bundle ID)
system("pluginkit -e ignore -i YourAppBundleID")
Before i used:
system("pluginkit -e use -i AppBundleID.FinderSync")
so just remove ".FinderSync" its working.

Linking an answer I found on the Apple developer forum:
https://forums.developer.apple.com/thread/77682
When your App is outside the Sandbox, you can use:
Objective-C:
system("pluginkit -e use -i <yourFinderExtensionBundleID>");
Swift:
let pipe = Pipe()
let task = Process()
task.launchPath = "/usr/bin/pluginkit"
task.arguments = ["-e", "use", "-i", "<yourFinderExtensionBundleID>"]
task.standardOutput = pipe
let file = pipe.fileHandleForReading
task.launch()
let result = NSString(data: file.readDataToEndOfFile(), encoding:

Related

Run ShellScript Without Authorization Popup for MAC Application

I am working on MAC Application in which I want to Remove Helper tool previously installed by my application.
I am using STPrivilegedTask to run my ShellScript.
Here is the code I am using to achieve this.
NSString *pathForUninstallFile = [[NSBundle mainBundle] pathForResource:#"Uninstall" ofType:#"sh"];
STPrivilegedTask *taskToRemoveHelperTool = [STPrivilegedTask launchedPrivilegedTaskWithLaunchPath:pathForUninstallFile arguments:[NSArray arrayWithObjects:#"Uninstall.sh", nil]];
and here is my ShellScript Unistall.sh
launchctl unload /Library/LaunchDaemons/com.bsecure.HelperTool.plist
rm /Library/LaunchDaemons/com.appleCompany.HelperTool.plist
rm /Library/PrivilegedHelperTools/com.appleCompany.HelperTool
security -q authorizationdb remove "com.appleCompany.readLicenseKey"
security -q authorizationdb remove "com.appleCompany.newMethod"
Now the above code works perfect but it ask user's Authorization Popup.
I want to run this script without this Authorization Popup.
I already search in another question but none of them work for me that's why I created this question.
So you want to run a task with escalated privileges without prompting the user for a password? If so, you're out of luck, it can't be done.

Finder Sync Badge Extension Handling

I am trying to create a Finder Sync Extension,in yosemite to show badge in files and folder.
I am on the move, but i have no idea how to turn off extension(remove from extensions list in preferences) when my containing application terminate. Any help is appreciated.
Try it..
Reload Directory in Finder
// Reload Finder (change the word directory to file if updating file)
NSAppleScript * update = [[NSAppleScript alloc] initWithSource:[NSString stringWithFormat:#"tell application \"Finder\" to update POSIX directory \"%#\"",path]];
[update executeAndReturnError:nil];
Code to Enable Extension (bundle ID)
system("pluginkit -e use -i com.xyz.finderExt")
Code to Disable Extension (bundle ID)
system("pluginkit -e ignore -i com.xyz.finderExt")
code to remove from extensions list in preferences
pluginkit -r "/Applications/App-name/Contents/Plugins/extension-name.appex"

Unable to find running application in Cocoa

I am getting the list of running applications in Cocoa with the following code:
for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) {
MNSLog(#"%#",[app localizedName]);
}
However an application I started from a terminal session is not appearing in the list ('Terminal' is well appearing). The application was started from the same user which is executing the cocoa code.Is my launched application under Terminal ? And in such a case how can I find its name and arguments ?Running ps in another terminal session show my process properly.
Use an NSTask to execute the ps Shell command. You can check the ps man page to determine which arguments you want to pass it based on the information you want to get back. Use NSPipe and NSFileHandle to get the results from the task.
If you want to do some filtering you can pipe the ps output through grep before your app picks up the result.
For your first question, I think NSWorkspace can only see apps that use the window server so you will only see Terminal, not the executables that it is running internally.
You can use sysctl or ps command to get a list of all BSD processes. Have look at unable to detect application running with another user

Launch Finder window with specific files selected

I'm trying to programmatically launch an OS X Finder window from an Xcode project. I need the window to open to a specific folder and have specific files within that folder automatically selected.
This is similar to the "Show in Finder" functionality used in Xcode and related apps.
Does anyone know how to do this in either Objective-C, Swift, AppleScript, or Finder command-line parameters?
Objective-C version:
NSArray *fileURLs = [NSArray arrayWithObjects:fileURL1, /* ... */ nil];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs];
$ open -R <path-to-reveal>
Another AppleScript flavor - the Finder's reveal command will both open a window to the containing folder and select the item(s). If there are multiple containing folders, multiple Finder windows will be opened.
tell application "Finder"
to reveal {someAlias, "path/to/POSIXfile" as POSIX file, etc}
Swift version:
let paths = ["/Users/peter/foo/bar.json"]
let fileURLs = paths.map{ NSURL(fileURLWithPath: $0)}
NSWorkspace.sharedWorkspace().activateFileViewerSelectingURLs(fileURLs)
I'm finding that activateFileViewerSelectingURLs is not working on Yosemite (at least when in separate space from Finder). It will cause a switch to the Finder's space but won't seem to select the URL. Using:
- (BOOL)selectFile:(NSString *)fullPath inFileViewerRootedAtPath:(NSString *)rootFullPath
will switch spaces from full screen app and select path.
When opening a file at path:
NSString* path = #"/Users/user/Downloads/my file"
NSArray *fileURLs = [NSArray arrayWithObjects:[NSURL fileURLWithPath:path], nil];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs];
Swift 3.2/4.0 Version:
NSWorkspace.shared.activateFileViewerSelecting([outputFileURL])
Reveal multiple files in Finder
As open -R <path-to-reveal> only works for single file.
We can use Apple Script instead.
From user866649's answer, we can port it to a shell script as the following:
osascript -e 'tell application "Finder" to reveal {"path/to/file1" as POSIX file, "path/to/file2" as POSIX file} activate'
Just created a utility script:
finder.sh
#!/usr/bin/env bash
join() {
local d=$1 s=$2
shift 2 && printf %s "$s${#/#/$d}"
}
lst=()
for f in "$#"; do
lst+=("\"$f\" as POSIX file")
done
files=$(join , "${lst[#]}")
osascript -e "tell application \"Finder\" to reveal {$files} activate"
Then try it:
chmod +x finder.sh
./finder.sh ~/Downloads ~/Desktop
It should open Finder and selects both Downloads and Desktop folder.

NSTask and Git -- Permissions issues

In my Cocoa application I'm trying to use NSTask to run some basic Git commands. Whenever I run a command that requires permissions (SSH keys) to access a remote (e.g. git push, git pull), it fails with the following error:
Permission denied (publickey). The remote end hung up unexpectedly
Running the same commands from Terminal works just fine, so I'm thinking that this might be an issue with NSTask not setting an environment variable that would be used somewhere in the process of accessing the ssh keys. I tried manually setting the HOME and USER environment variables like this:
[task setEnvironment:[NSDictionary dictionaryWithObjectsAndKeys:NSHomeDirectory(), #"HOME", NSUserName(), #"USER", nil]];
But this has no effect. Is there any particular environment variable I have to set in NSTask for this to work properly?
EDIT: Thanks to Dustin's tip, I got a little bit further in figuring this out. I used the env command to list the environment variables for my current session and I found this:
SSH_AUTH_SOCK=/tmp/launch-DMQopt/Listeners
To test, I copied that path and set it as an environment variable of NSTask and ran the code again, and this time it worked! That said, I'm certain that SSH_AUTH_SOCK changes for each session so I can't just hardcode it. How do I dynamically generate/retrieve this variable?
You could try and follow the tutorial "Wrapping rsync or SSH in an NSTask" (from Ira), which does mention SSH_AUTH_SOCK variable:
Since writing this post I've realised that I omitted an important additional step in setting up the environment variables for the NSTask.
In order to make passwordless key-based authentication work it's necessary to grab the SSH_AUTH_SOCK variable from the user's environment and include this in the NSTask's environment.
So, when setting environment variables for example;
NSTask *task;
NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
// Environment variables needed for password based authentication
NSMutableDictionary *env = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"NONE", #"DISPLAY", askPassPath, #"SSH_ASKPASS",
userName,#"AUTH_USERNAME",
hostName,#"AUTH_HOSTNAME",
nil];
// Environment variable needed for key based authentication
[env setObject:[environmentDict objectForKey:#"SSH_AUTH_SOCK"] forKey:#"SSH_AUTH_SOCK"];
// Setting the task's environment
[task setEnvironment:env];
However, the OP indragie comments:
I had tried this earlier but since it was being invoked with XCode, the SSH_AUTH_SOCK env var. wasn't being passed to it.
Opening the app from Finder corrects this issue.
With askPassPath being the path for the Askpass executable, which is included as part of the application’s main bundle. (In order to do this, find the executable under “Products” in xcode, and then drag it into “Copy Bundle Resources” on the main application’s target.)
// Get the path of the Askpass program, which is
// setup to be included as part of the main application bundle
NSString *askPassPath = [NSBundle pathForResource:#"Askpass"
ofType:#""
inDirectory:[[NSBundle mainBundle] bundlePath]];