MacOS - requesting audio/camera permission - wrong app title shown? - macos

We are asking for permission to use audio and camera in our Mac app. In general this works OK.
But there is one quirk. If we launch our app from another app, such as from Terminal -
When the permission prompt comes up, the app that is identifed as requesting access is the top level application ie. Terminal, not our app name.
If our app is run independently from finder (etc), the correct title shows up in the permission prompt.
I thought it might be a missing CFBundleDisplayName, but that doesn't help.
void PERMISSIONS::checkAudioPermission() const
{
if (#available(macOS 10.14, *))
{
AVAuthorizationStatus audioAuthStatus =
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
if (audioAuthStatus != AVAuthorizationStatusAuthorized)
{
// If the user has not definitively said Yes or No, ask them to do so.
if (audioAuthStatus == AVAuthorizationStatusNotDetermined)
{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted)
{
if (granted)

Unfortunately, this is actually a vulnerability that Apple has never fixed, which could be exploited by malware. This is discussed by Patrick Wardle at his site:
https://objective-see.com/blog/blog_0x2F.html
Therefore, I highly encourage you to file a bug, so that this can be fixed.

Related

AXUIElementCopyAttributeNames returns cannotComplete in Cocoa App but works well in playground

I'm trying to build an utility in navigating to any visible & enabled ui-element without mouse or trackpad, so I have to enumerate those elements first.
I did some research and found maybe Accessibility APIs would help me to reach my goal. Then I tried them in playground as below :
//...
let key: String = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String
let options = [key: true]
let enabled = AXIsProcessTrustedWithOptions(options as CFDictionary)
if !enabled {
print("Not permitted")
NSApp.terminate(nil)
}
//...
let element = AXUIElementCreateApplication(pid as! pid_t)
var ans : CFArray?
let e = AXUIElementCopyAttributeNames(element, &ans)
if e == .success, let names = ans as? [String] {
print(names)
}
//...
It works well in playground. Then I created a new Cocoa App, copied codes above into it, ran it in debug mode, and AXUIElementCopyAttributeNames return an AXError - cannotComplete.
It's very sure Xcode is enabled in System Preferences > Security and Privacy > Privacy > Accessibility. AXIsProcessTrustedWithOptions always returns true seems that it's not an issue of permission.
How can I make it works? Did I miss any necessary setting?
I'm not good in English, sorry about that.
I've solved this issue by turning off App Sandbox. I'm still curious why there is not accessibility entitlement in sandbox. Given that I have no plan to put my app in Mac App Store, to turn off App Sandbox is a solution for me.
Thanks everyone.
"With App Sandbox, you can and should enable your app for accessibility, as described in Accessibility Programming Guide for OS X. However, you cannot sandbox an assistive app such as a screen reader, and you cannot sandbox an app that controls another app."
Excerpt from https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/DesigningYourSandbox/DesigningYourSandbox.html
Playgrounds are not sandboxed and that is why it worked there.

AXIsProcessTrustedWithOptions doesn't return true even when the app is ticked in accessibility

As this question answered, I use AXIsProcessTrustedWithOptions with no option to test if my app has been enabled in the accessibility panel. If not, prompt the window to let users to enable it. There is button bound to this test call as well, so users don't need to close the UI and reopen it.
But sometimes, even after a user enables the app, AXIsProcessTrustedWithOptions still returns false. When I check the sqlite database, it shows my app as "kTCCServiceAccessibility|com.abc.def|0|1|1||". The first 1 digit indicates it has been allowed apparently. But the api call still returns false. At this point, if I close the UI, the app is unticked in the accessibility panel which causes a loop so users can never pass this step.
Another thing is this app is actually a version 2 of the previous app. But the user did replace the bundle with the v2 one. Not sure if this is related.
Any idea why this would happen?
NSDictionary *options = #{(id)kAXTrustedCheckOptionPrompt: #NO};
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
if (!accessibilityEnabled) {
NSString *urlString = #"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
Update: In Xcode, Signing & Capabilities, make sure you have selected a Team and Signing Certificate.
Original answer:
I experienced this when developing an app on macOS 10.14.5 in Xcode trying both AXIsProcessTrusted and AXIsProcessTrustedWithOptions .
After wasting a lot of time trying all sorts of things, including different options for AXIsProcessTrustedWithOptions, logging out/in, rebooting, praying, and crying, I eventually shut down my machine and powered it back on in frustration. Then it worked. Hopefully nobody else has this same experience.

Custom URL Scheme for Settings on iOS 10?

Any Idea what happened to the Setting's Custom URL Scheme on iOS 10?
Is Apple still giving acess to third-pary apps to launch iOS Settings from the app via URL Scheme on iOS10? The Old URL scheme are not working anymore!
None of the previous methods for launching the root "Settings" app on iOS 8+ were officially supported by Apple, so unfortunately we can't rely on them. It's also possible that apps that relied on the undocumented behaviors could be rejected during App Store review, even if others have been approved--even if the same app had been previously approved!
I've been unable to discover any workaround either, so it seems your choices are:
Open the app-specific URL (as detailed in many places, including #alvin-varghese 's answer above), and then ask the user to navigate backwards. (A terrible user experience, since it involves the user knowing to scroll up from the app list into the main settings sections.)
Use an instructional screen or alert in your app to educate users on how to find it themselves. (Not much better, but at least aren't dropped into an unfamiliar context with no waypoints.)
There doesn't seem to be an officially supported way of making this happen in iOS 10, and as you pointed out, it seems like the old ways (adding prefs scheme to your Info.plist file and using openURL(_:)) do not work any more.
From iOS 10 some of the things are changed,
So, for something like bluetooth you need to write below lines of code,
Swift 3.0 :
func openBluetoothSettings(){
let urlBTSet = URL(string: "App-Prefs:root=Bluetooth")
let objApp = UIApplication.shared
objApp.openURL(urlBTSet!)
}
Objective-c
-(void) openBluetoothSettings{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"App-Prefs:root=Bluetooth"]];
}
So, In above code what they have changed is string need to add "App-Prefs:root=Bluetooth" (This the example of opening bluetooth settings)
Don't forgot : 'Goto taget -> info -> URL Types -> Add "prefs" in URL Schemes'
Use this one, it works.
if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared().open(url, options: [:], completionHandler: nil)
}

Set MacOS X menubar app to launch at startup

I have a sandboxed menubar application (no dock icon) that in it's preferences window allows the user to check a checkbox to have the app launch at login. I used to use the LSSharedFileList api, but as this is not allowed anymore for sandboxed apps, I've migrated to using SMLoginItemSetEnabled. What I've found is that although the app will launch at login, as expected, if I go back into the Preferences and uncheck and re-check the launch at login checkbox, I get a second instance of my menubar app launched.
Here's my helper app code (in its app delegate):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSString * path = [[[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent]
stringByDeletingLastPathComponent]
stringByDeletingLastPathComponent]
stringByDeletingLastPathComponent];
[[NSWorkspace sharedWorkspace] launchApplication:path];
[NSApp terminate:nil];
}
Here is the code in my preferences window (main app):
- (IBAction)toggleLoginStatus:(NSButton*)sender{
if(!SMLoginItemSetEnabled((__bridge CFStringRef)#"myAppBundleIdentifier", (BOOL)[sender state])){
NSLog(#"Dagnabit!");
}
}
After the second instance launches, unchecking/re-checking the checkbox does not launch anymore instances. Does anyone have any idea what's going on?
Thanks
I've found the answer. None of the tutorials I looked at mentioned this, but in the docs for SMLoginItemEnabled says this:
The Boolean enabled state of the helper application. This value is
effective only for the currently logged in user. If true, the helper
application will be started immediately (and upon subsequent logins)
and kept running. If false, the helper application will no longer be
kept running.
So I'll have to check if the app is running already before allowing the helper to launch it.
I spent almost two days figuring out this. Finally dropped the idea to support launch at login.
Although it is just a case of Yes or no in the Front end, developer on the other hand has to spent huge amount of time(Adding a helper application and doing all those project settings in both the apps). The process in early days (before sandboxing)was pretty much simpler than it is now.
Also when the application is added to the 'launch at login' list, there is no way to verify because the application is not listed in system preferences -> users -> login items, as it was before sand boxing.
I am also facing a strange problem where I added helper app to the launch at login and then uninstalled the helper, but the activity monitor still shows helper app in the list, after each reboot.
Lets hope apple adds something like LaunchAtLoginController in coming future.

Close other applications using swift

Is there a way to close running applications in swift? For instance, if the application I create needs to close safari.
Here's a Swift 5 version for closing running applications without using AppleScript (AppleScript is a perfect way but it isn't the only way), Safari is used as the example in this case:
let runningApplications = NSWorkspace.shared.runningApplications
if let safari = runningApplications.first(where: { (application) in
return application.bundleIdentifier == "com.apple.Safari" && application.bundleURL == URL(fileURLWithPath: NSWorkspace.shared.fullPath(forApplication: "Safari")!)
}) {
// option 1
safari.terminate()
// option 2
kill(safari.processIdentifier, SIGTERM)
}
SIGTERM instead of SIGKILL, referencing from here
Of course, make sure you notify the user of this activity since this may cause negative impact on the user-experience (for example, user-generated contents in the targeted application are not saved before terminating)
It is certainly possible via an applescript directly IF:
your app is not running sandboxed (note that if you plan to distribute it via the App Store, your app will be sandboxed)
OR
your app has the necessary entitlements to use applescript: com.apple.security.scripting-targets (apple needs to approve that AND you need to know which apps to target. this isn't a blanket permission)
then
https://apple.stackexchange.com/questions/60401/how-do-i-create-an-applescript-that-will-quit-an-application-at-a-specific-time
Can you execute an Applescript script from a Swift Application
if you aren't going for App Store complicity anyways, you might also use NSTask directly
scripts / code snippets:
How to force kill another application in cocoa Mac OS X 10.5
Can you execute an Applescript script from a Swift Application
short & sweet: technically yes, 'politically' maybe :D

Resources