Indie Notes 2018-09-11T19:27:47Z macOS Mojave gets new APIs around AppleEvent sandboxing – but AEpocalypse still looms <p> AppleEvent sandboxing – which was announced in the WWDC 2018 session <a href="https://developer.apple.com/wwdc18/702" target="_blank">"Your Apps and the Future of macOS Security"</a> – affects every app that sends AppleEvents to other apps. </p> <p> Back in June, I wrote that the new <a href="/blog/2018/06/apple-event-sandboxing-in-macos-mojave">Apple Event sandboxing in macOS Mojave lacks essential APIs</a> and highlighted problems with the implementation. </p> <p> Since then, Apple has made a couple of changes to AppleEvent sandboxing. </p> <h4>Usage Descriptions for AppleEvents</h4> <p style="text-align: center;"> <img src="/blog/2018/08/images/authorizationAlertBeta8~4dc34d22d3c55dda982116b819d3cd61b21c9de9.jpg" class="responsive" srcset="/blog/2018/08/images/authorizationAlertBeta8~4dc34d22d3c55dda982116b819d3cd61b21c9de9.jpg 1x, /blog/2018/08/images/authorizationAlertBeta8@2x~55859b58400456388b75e168769727fcdf9abcaf.jpg 2x"> </p> <p> Usage descriptions provide context around an app's request for a particular permission. Typically, it is shown as part of authorization prompts that ask for permission to access sensitive areas like photos, camera, contacts or calenders. </p> <p> Since macOS Mojave beta 6, a usage description for AppleEvents can (and often must) now be provided for the <code>NSAppleEventsUsageDescription</code> key in an app's Info.plist. Daniel Jalkut has an excellent <a href="https://indiestack.com/2018/08/apple-events-usage-description/" target="_blank">blog post</a> showing you how. </p> <h5>Caveats</h5> <p> <ul style="margin-left: 0.5rem;"> <li>If you build your app with the 10.14 SDK, the <code>NSAppleEventsUsageDescription</code> key is <b>required</b>. If it is not provided, trying to send an AppleEvent to another, running app will always return error <code>errAEEventNotPermitted</code> (-1743). The key is only <b>optional</b> when using older SDKs.</li> <li>If you use AppleEvents in a helper app that's contained in your app's bundle, macOS may expect and use the value for the <code>NSAppleEventsUsageDescription</code> key in <i>the app's</i> Info.plist, not the helper app's Info.plist.</li> <li>Apps can only provide one usage description. It is not possible to provide different usage descriptions for every target app. I still hope we'll get that capability at some point in the future.</li> </ul> </p> <h4>Determining and prompting for user consent</h4> <p> Two new APIs now enable apps to time when the user is asked for consent and to determine the current authorization status. </p> <h5>AEDeterminePermissionToAutomateTarget</h5> <p> macOS Mojave beta 7 introduced <code>AEDeterminePermissionToAutomateTarget</code>, a new API that allows apps to determine whether they're authorized to send an AppleEvent to another app: </p> <p> <div class="code-box wraps">OSStatus AEDeterminePermissionToAutomateTarget( const AEAddressDesc* target, AEEventClass theAEEventClass, AEEventID theAEEventID, Boolean askUserIfNeeded ); </div> </p> <p> The new function expects a reference to the target app to be provided as <code>AEAddressDesc</code>. </p> <p> Permissions can be determined for a specific <code>AEEventClass</code>/<code>AEEventID</code> combination. Alternatively, passing <code>typeWildCard</code> for both checks if <i>every</i> AppleEvent can be sent to the target app. This distinction is important since certain AppleEvents don't require authorization from the user. <p> Finally, apps can choose to just check their current authorization status - or prompt the user to obtain permission as needed. </p> <p> Here's a simple example that determines if the calling app currently has permission to send AppleEvents to iTunes - and prompts if not: </p> <div class="code-box wraps">OSStatus status; NSAppleEventDescriptor *targetAppEventDescriptor; targetAppEventDescriptor = [NSAppleEventDescriptor descriptorWithBundleIdentifier:@"com.apple.iTunes"]; status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true); </div> </p> <p> Possible return values of <code>AEDeterminePermissionToAutomateTarget</code> include: </p> <p> <ul style="margin-left: 2rem;"> <li><code>errAEEventNotPermitted</code> (-1743): the user has declined permission.</li> <li><code>errAEEventWouldRequireUserConsent</code> (-1744): user consent is required for this, but the user has not yet been prompted for it. You need to pass <code>false</code> for <code>askUserIfNeeded</code> to get this.</li> <li><code>noErr</code> (0): the app is authorized to send AppleEvents to the target app.</li> <li><code>procNotFound</code> (-600): the specified target app is not currently running.</li> </ul> </p> <h5>kAEDoNotPromptForUserConsent</h5> <p> Also new in macOS Mojave beta 7 is <code>kAEDoNotPromptForUserConsent</code>, a flag that can be added to the <code>AESendMode</code> that's passed to <code>AESend()</code>. </p> <p> If the AppleEvent sent that way would require user consent, the user is not prompted for consent. Instead <code>AESend()</code> will return <code>errAEEventWouldRequireUserConsent</code>. </p> <h5>Caveats</h5> <p> <ul style="margin-left: 2rem;"> <li> <p> <b>target apps need to run:</b> to determine the current status or request authorization to send AppleEvents to another app, that app still needs to be running. Unfortunately, this limitation stands in the way of good and consistent user experiences: </p> <p><ul> <li><p><b>apps can't adapt their UI:</b> if an app wants to adapt its UI depending on whether it's authorized to send AppleEvents to a target app, it can't determine its current status unless that target app is running.</p> <p> A UI, however, that only dynamically adapts <i>sometimes</i> (= while the target app is running) is inconsistent and confusing.</p> </li> <li><p><b>apps can't preauthorize:</b> apps that want to provide a good user experience around authorizations as part of onboarding would greatly benefit from the ability to prompt for authorization without having to launch the targeted app. It just isn't a good user experience having to.</p> <p>Unfortunately, for apps like mine that target a large number of apps and are meant to be useable without a keyboard, mouse or trackpad attached to the Mac, there are no realistic alternatives to preauthorization during onboarding. Right now that means launching each targeted app and then prompting the user for consent.</p> <p>Authorization "on-the-go" - prompting for authorization when target apps are used with them for the first time - is not an option here.</p> </li> <li><b>caching results is not a solution:</b> users can grant or revoke their authorization to control another app in System Preferences at any time, so that any previously cached return values from <code>kAEDoNotPromptForUserConsent</code> no longer reflect the status quo, leading to inconsistencies in apps relying on them. </li> </ul></p> </li> <li> <p> <b>prompts can't be repeated:</b> macOS will show only one prompt per target app. If users decline an authorization in error, or change their minds later, the only way to do so is in a remote corner of System Preferences. The best possible user experience then looks somewhat like this: </p> <p style="text-align: center;"> <img src="/blog/2018/08/images/manualAuthorization~65a1a78d164d615619f9c78f65a24d77d199229a.jpg" class="responsive" srcset="/blog/2018/08/images/manualAuthorization~65a1a78d164d615619f9c78f65a24d77d199229a.jpg 1x, /blog/2018/08/images/manualAuthorization@2x~006314f7902097f5447d3f96fb05cea8264420e5.jpg 2x"> </p> <p> Bringing up the prompt only once per target app definitely makes sense when the prompt is triggered <i>implicitly</i>, by way of sending an AppleEvent. Otherwise, apps unaware of AppleEvent sandboxing that keep sending AppleEvents to a target app could bring up the prompt very frequently. </p> <p> However, when using <code>AEDeterminePermissionToAutomateTarget</code> to <i>explicitely</i> request the prompt, it should be possible to show the prompt more than once to avoid an unnecessarily bad user experience. If abuse of the ability to repeatedly show the prompt is a concern, a "Don't show this again" checkbox could be added to it to deal with that. </p> </li> <a name="noStatusWithoutPrompting"></a> <li> <p> <b>current status can't be determined without (risking to) prompt</b>: As of 10.14 beta 9, you need to pass <code>true</code> for <code>askUserIfNeeded</code> to get <code>errAEEventNotPermitted</code>, which can bring up the prompt. </p> <p> Passing <code>false</code>, <code>errAEEventWouldRequireUserConsent</code> is returned even if the user was previously prompted and declined permission. </p> <p> In consequence, even for running applications, <code>AEDeterminePermissionToAutomateTarget</code> can't be used to silently determine the current status. To be usable for that, it would need to return <code>errAEEventNotPermitted</code> in case consent is required <i>and</i> the user has already been prompted about it and denied. This feels like a bug. Paulo Andrade has filed <a href="http://www.openradar.me/radar?id=4945773766639616" target="_blank">a radar</a> about it. </p> </li> <li><b>calls can be blocking:</b> if <code>AEDeterminePermissionToAutomateTarget</code> prompts the user for authorization, it blocks the calling thread until the user has made a choice. If this is an issue in your app, consider moving the call to a different thread: <code>AEDeterminePermissionToAutomateTarget</code> is thread-safe.</li> <li><b>usage description required:</b> if <code>AEDeterminePermissionToAutomateTarget</code> always returns <code>errAEEventNotPermitted</code> (-1743) even if you target apps for which no prompt has been shown so far, your Info.plist (or the Info.plist of the app containing the app) likely lacks a usage description for AppleEvents (see above for more on this).</li> <li><b>documentation is hard to find:</b> at the time of writing, no documentation - or even a hint at the existence of the new API - exists in Apple's new documentation. Detailed documentation can, however, be found in the <code>AppleEvents.h</code> header file.</li> </ul> </p> <h4>Configuration Profiles</h4> <p> Apple recently updated their <a href="https://developer.apple.com/enterprise/documentation/Configuration-Profile-Reference.pdf" target="_blank">Configuration Profile Reference</a>, which now includes a <i>Privacy Preferences Policy Control Payload</i>. </p> <p> Next to <code>AppleEvents</code>, it also allows providing an array of <i>Identity Dictionaries</i> for each of these <i>Services</i>: <code>AddressBook</code>, <code>Calendar</code>, <code>Reminders</code>, <code>Photos</code>, <code>Camera</code>, <code>Microphone</code>, <code>Accessibility</code>, <code>PostEvent</code>, <code>SystemPolicyAllFiles</code>, <code>SystemPolicySysAdminFiles</code>. That should cover pretty much all of the new sandboxing features in Mojave. </p> <p> For <code>AppleEvents</code>, detailed information like the code requirements of the sender and the target app is required in the <i>Identity Dictionary</i>. Here's an example excerpt of a payload that would allow Remote Buddy to send AppleEvents to iTunes: </p> <div class="code-box wraps">&lt;key&gt;PayloadType&lt;/key&gt; &lt;string&gt;com.apple.TCC.configuration-profile-policy&lt;/string&gt; &lt;key&gt;Services&lt;/key&gt; &lt;dict&gt; &lt;key&gt;AppleEvents&lt;/key&gt; &lt;array&gt; &lt;dict&gt; &lt;key&gt;Identifier&lt;/key&gt; &lt;string&gt;com.iospirit.remotebuddy&lt;/string&gt; &lt;key&gt;IdentifierType&lt;/key&gt; &lt;string&gt;bundleID&lt;/string&gt; &lt;key&gt;CodeRequirement&lt;/key&gt; &lt;string&gt;anchor apple generic and identifier "com.iospirit.remotebuddy" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = UH96K9N25C)&lt;/string&gt; &lt;key&gt;AEReceiverIdentifier&lt;/key&gt; &lt;string&gt;com.apple.iTunes&lt;/string&gt; &lt;key&gt;AEReceiverIdentifierType&lt;/key&gt; &lt;string&gt;bundleID&lt;/string&gt; &lt;key&gt;AEReceiverCodeRequirement&lt;/key&gt; &lt;string&gt;identifier "com.apple.iTunes" and anchor apple&lt;/string&gt; &lt;key&gt;Allowed&lt;/key&gt; &lt;true/&gt; &lt;/dict&gt; &lt;/array&gt; &lt;/dict&gt; </div> <p> With no way to whitelist apps and no way to ask the user for authorization without the targeted apps running, building a configuration profile for users and saving them a ton of clicks and frustration is definitely a tempting idea. I've also had it. </p> <p> However, profiles with this payload fail to install as they "<i>must originate from a user approved MDM server</i>": </p> <p style="text-align: center;"> <img src="/blog/2018/08/images/profilesRequireMDMServer~e96d841576682929cd83f7ea2a2559f5a53a7ab1.jpg" class="responsive" srcset="/blog/2018/08/images/profilesRequireMDMServer~e96d841576682929cd83f7ea2a2559f5a53a7ab1.jpg 1x, /blog/2018/08/images/profilesRequireMDMServer@2x~b273e6850c538867bfe46deda64972dde9caec19.jpg 2x"> </p> <h4>Updated authorization prompts</h4> <p> When AppleEvent authorization prompts first appeared in beta 2, the title just read: <blockquote style="font-size: 1.75rem; font-weight: bold; margin-bottom: 0px;">"Some.app" would like to control the application "Other.app".</blockquote> </p> <p> In the current beta, the title is now a lot longer: <blockquote style="font-size: 1.75rem; font-weight: bold; margin-bottom: 0px;">"Some.app" wants access to control "Other.app". Allowing control will provide access to documents and data in "Other.app", and to perform actions within that app.</blockquote> </p> <p> The additional sentence provides valuable context to inform the decision of people who aren't as familiar with macOS security, but it makes the title way too long. </p> <p> Below is an image that shows this and other issues I found with the current authorization prompt, as well as suggestions on how it could be improved. </p> <p style="text-align: center;"> <img src="/blog/2018/08/images/authorizationAlertImproved~c41b6e1228151f0239cf5ac9c73a895f25de1149.jpg" class="responsive" srcset="/blog/2018/08/images/authorizationAlertImproved~c41b6e1228151f0239cf5ac9c73a895f25de1149.jpg 1x, /blog/2018/08/images/authorizationAlertImproved@2x~5f6be354165312bba522bf0decd9172d08f0660b.jpg 2x"> </p> <p> I've filed a bug on this (<a href="rdar://43897386">Radar</a>, <a href="https://openradar.appspot.com/radar?id=5063613442162688" target="_blank">Open Radar</a>). </p> <h4>Resetting the privacy database</h4> <p> Apple provides <code>tccutil</code> - a utility to manage the privacy database. To reset AppleEvent authorizations for all apps, use: </p> <div class="code-box wraps">tccutil reset AppleEvents</div> <p> Going by the man page, it should also be possible to reset the AppleEvent authorizations for a single app by appending its bundle identifier like this: </p> <div class="code-box wraps">tccutil reset AppleEvents com.iospirit.remotebuddy</div> <p> However, that command currently fails with an error, stating there is no such bundle identifier. </p> <h4>What's still missing?</h4> <p> Apple's willingness to act on the <a href="/blog/2018/06/apple-event-sandboxing-in-macos-mojave">issues of the original AppleEvent sandboxing implementation</a> in a matter of weeks deserves respect and my heartfelt thanks goes out to everyone at Apple who made that possible. </p> <p> The new APIs solve the <i>most</i> pressing issues. </p> <p> For many apps, however, it remains impossible to provide a user experience on par with that of previous macOS versions. Still missing for that: <ul> <li>the ability <i>for users</i> to <b>whitelist apps</b> to exempt them from AppleEvent sandboxing - much like what "Full Disk Access" does for file system access. This is especially important for apps that target many apps - or an open-ended list of apps. Also, users of legacy software that's not updated for macOS Mojave may find themselves in need of a whitelisting option.</li> <li><b>an improved version of <code>AEDeterminePermissionToAutomateTarget</code></b> that can<ul> <li>determine the current authorization status and prompt for authorization <b>without requiring the target app to run</b></li> <li>show the authorization <b>prompt for a target app more than once</b></li> </ul></li> </ul> </p> <hr> <h4>AEpocalypse still looms</h4> <p> AppleEvents are a powerful and important IPC mechanism, differentiator and defining feature of the Mac. They're at the heart of AppleScript, Mac automation, utilities, accessibility tools and pro workflows. </p> <p> The introduction of sandboxing will have a lasting impact on how AppleEvents work and how they can be used by apps. It has the potential to change the character of the technology as well as the Mac platform as a whole. </p> <h5>Summary of the current status</h5> <p> As of beta 9 - possibly the last beta before Mojave is likely released to the public in roughly a month - here's where things stand: <ul> <li><b>AppleEvent sandboxing still lacks essentials like whitelisting</b> or pre-authorization to target apps that are not currently running (see above)</li> <li>other than for a short segment in <a href="https://developer.apple.com/wwdc18/702" target="_blank">WWDC 2018 session 702</a>, <b>there's no documentation, guide, release note, tech note or transition guide</b> about AppleEvent sandboxing.</li> <li> <p><b>building an app with the macOS 10.14 SDK can break its use of AppleEvents</b>: AppleEvent and AppleScript APIs will return <code>errAEEventNotPermitted</code> - until a <code>NSAppleEventsUsageDescription</code> – which isn't mentioned or documented anywhere in Apple's documentation or headers – is added to the correct Info.plist.</p> </li> <li>the <b><a href="http://codeworkshop.net/objc-diff/sdkdiffs/macos/10.14b6/CoreServices.html" target="_blank">new APIs</a>, added in beta 7</b>, are a relatively recent addition that many developers will find useful. They are, however, <b>not obvious to find</b> - and documentation for them is only available in the headers, but not Apple's developer documentation.</li> </ul> </p> <h5>Practical impact</h5> <p> Control over the timing of authorization prompts is now possible, but still constrained. </p> <p> No support for whitelisting apps means users of utilities that target many apps will not be able to escape a <a href="https://www.youtube.com/watch?v=VuqZ8AqmLPY" target="_blank">Vista-esque</a> "bombardment" with authorization prompts that keep interrupting their work. It's not hard to see that affected apps will get one-star reviews, loose users and sales - through no fault of their own. </p> <p> No support for whitelisting apps also means there's no fallback for legacy apps that - for any reason - don't work well, or at all, with AppleEvent sandboxing. </p> <p> The lack of documentation around AppleEvent sandboxing means developers either aren't aware of the changes - or can't know in time how they can prepare their apps for them. </p> <h5>Apple's options from here</h5> <p> AppleEvent sandboxing, as of Mojave beta 9, is not in a good shape. The addition of new APIs in beta 7 telegraphed that Apple is still working on it. But it's unclear what changes are still in the pipeline - and whether Apple can make enough progress before Mojave's public release. </p> <p> I hope Apple can at least squeeze in support for whitelisting before public release as it's really a catch-all for issues with the new mechanism. </p> <p> I feel, though, that the most responsible way for Apple to handle this situation would follow the playbooks for Group FaceTime and 32 Bit deprecation: <a href="https://www.macrumors.com/2018/08/13/group-facetime-removed-ios-12-and-macos-mojave/">postpone the feature</a> until it has matured - and <a href="https://developer.apple.com/library/archive/releasenotes/General/RN-macOS-10.13.4/index.html#//apple_ref/doc/uid/TP40017702-CH1-DontLinkElementID_1">make it available to developers behind a feature toggle</a> until then. </p> <p> Ultimately, I'd like Apple to reconsider its approach when making changes like these to the foundation of macOS: introduce it at WWDC, but put it behind a <a href="https://developer.apple.com/library/archive/releasenotes/General/RN-macOS-10.13.4/index.html#//apple_ref/doc/uid/TP40017702-CH1-DontLinkElementID_1">feature toggle</a>. Leverage the developer community. Enter into a dialogue to learn about unintended effects of the change, missing or bad APIs. Iterate. Make changes where needed. Provide comprehensive documentation well in advance. Then, at WWDC the year after, remove the feature toggle and make the change permanent. </p> <p> Giving foundational changes like these a full year to mature – and developers to adapt their apps to them – would lead to all-around better results, less stress, anxiety and frustration for developers, a higher degree of stability and a better experience for users. </p> <hr> <p> <i>I'd like to thank <a href="https://twitter.com/folivora_ai" target="_blank">Andreas Hegenberg</a>, <a href="https://twitter.com/daniel_a_a" target="_blank">Daniel Alm</a> and <a href="https://twitter.com/DebugStr" target="_blank">Jens Miltner</a> for sharing their discoveries and insights with me. This blog post would be far less comprehensive without them.</i> </p> <a name="updates"></a> <hr> <h4>Updates</h4> <div class="row"> <div class="nine columns" style="font-weight: bold; font-size: 2.0rem;">How to open System Preferences' Automation section</div> <div class="three columns" style="font-weight: normal; color: #aaa; text-align: right; font-size: 2.0rem;">Sep 2, 2018</div> </div> <p> Using the <code>x-apple.systempreferences</code> <a href="https://macosxautomation.com/system-prefs-links.html" target="_blank">URL scheme</a>, it's possible to open System Preferences right at "Security &amp; Privacy &gt; Privacy &gt; Automation" by opening this URL: </p> <p> <code><a href="x-apple.systempreferences:com.apple.preference.security?Privacy_Automation">x-apple.systempreferences:com.apple.preference.security?Privacy_Automation</a></code> </p> <p> In code: <div class="code-box wraps">[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Automation"]];</div> </p> <p><i>Thanks to <a href="https://twitter.com/pfandrade_/status/1036028158150094848" target="_blank">Paulo Andrade</a> and Shane Stanley for independently sharing this hint with me.</i></p> <hr> <div class="row"> <div class="nine columns" style="font-weight: bold; font-size: 2.0rem;"><code>AEDeterminePermissionToAutomateTarget</code> can't be used to "silently" determine authorization status – not even for running apps</div> <div class="three columns" style="font-weight: normal; color: #aaa; text-align: right; font-size: 2.0rem;">Sep 2, 2018</div> </div> <p> As of 10.14 beta 9, you need to pass <code>true</code> for <code>askUserIfNeeded</code> to get <code>errAEEventNotPermitted</code>, which can bring up the prompt. </p> <p> Passing <code>false</code>, <code>errAEEventWouldRequireUserConsent</code> is returned even if the user was previously prompted and declined permission. </p> <p> In consequence, even for running applications, <code>AEDeterminePermissionToAutomateTarget</code> can't be used to silently determine the current status. To be usable for that, it would need to return <code>errAEEventNotPermitted</code> in case consent is required <i>and</i> the user has already been prompted about it and denied. This feels like a bug. Paulo Andrade has filed <a href="http://www.openradar.me/radar?id=4945773766639616" target="_blank">a radar</a> about it. </p> <p><i>Thanks to <a href="https://twitter.com/pfandrade_/status/1036028158150094848" target="_blank">Paulo Andrade</a> for pointing this out to me.</i></p> <hr> <div class="row"> <div class="nine columns" style="font-weight: bold; font-size: 2.0rem;">Further reading</div> <div class="three columns" style="font-weight: normal; color: #aaa; text-align: right; font-size: 2.0rem;">Sep 2, 2018</div> </div> <ul> <li><a href="https://mjtsai.com/blog/2018/08/31/aedeterminepermissiontoautomatetarget-added-but-aepocalyse-still-looms/" target="_blank">AEDeterminePermissionToAutomateTarget Added, But AEpocalypse Still Looms</a> – Michael J. Tsai's excellent mix of quotes, links and commentary on the topic</li> <li><a href="https://derflounder.wordpress.com/2018/08/31/creating-privacy-preferences-policy-control-profiles-for-macos/" target="_blank">Creating Privacy Preferences Policy Control profiles for macOS</a> – Rich Trouton</li> <li><a href="https://www.shirt-pocket.com/blog/index.php/shadedgrey/comments/macos_mojave_opening_new_vistas_in_security_for_mac_users/" target="_blank">macOS Mojave: Opening New Vistas in Security for Mac Users</a> – Dave Nanian, Shirt Pocket<li> <li><a href="https://sixcolors.com/link/2018/08/a-looping-mac-automation-apocalypse" target="_blank">A looming Mac automation apocalypse?</a> – Jason Snell, SixColors</li> </ul> 2018-08-30T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2018/08/new-apple-event-apis-in-macos-mojave Apple Event sandboxing in macOS Mojave lacks essential APIs <p> In the WWDC 2018 session <a href="https://developer.apple.com/wwdc18/702" target="_blank">"Your Apps and the Future of macOS Security"</a>, Apple announced big changes to macOS security. </p> <p> One of them - and possibly the one with the biggest impact: <b>apps can no longer send Apple Events to other apps without user authorization</b>. </p> <p> Apple argues that Apple Events (which AppleScript uses under the hood) can be used to get access to otherwise protected user data in other apps, so the user should be prompted for authorization. </p> <p> I concur. However, the current implementation turns out to be problematic for <b>many</b> legitimate, privacy-respecting apps in (at least) the automation, accessibility, device management, utility and remote control categories. </p> <h4>The current implementation</h4> <p style="text-align: center;"> <img src="/blog/2018/06/images/MojaveAEAlert~4afd9025ed9f4f50964125627bf8aa350bf46dca.jpg" class="responsive"> </p> <p> The first time <i>Sender.app</i> sends an Apple Event to <i>Recipient.app</i>: </p> <p> <ul style="margin-left: 2rem;"> <li>the <i>Sender.app</i> thread sending the Apple Event is blocked</li> <li><i>Recipient.app</i> is launched, if it's not already running</li> <li>the user is prompted for authorization</li> </ul> </p> <p> The user can then choose between these two options: </p> <p> <ul style="margin-left: 2rem;"> <li><b>Don't Allow</b>: the thread resumes execution. The error code <code>errAEEventNotPermitted</code> (-1743) is returned to <i>Sender.app</i> for this and other Apple Events sent to <i>Recipient.app</i> in the future.</li> <li><b>OK</b>: the thread resumes execution. This - and future Apple Events from <i>Sender.app</i> to <i>Recipient.app</i> - are delivered without prompting the user again.</li> </ul> </p> <p> Noteworthy: </p> <p> <ul style="margin-left: 2rem;"> <li><b><i>Recipient.app</i> is launched every time</b> an Apple Event is sent in its direction - even if the user has chosen "Don't allow" before and the Apple Event isn't delivered.</li> <li>the <b>user is only ever prompted once</b> for every sender / recipient pair.</li> </ul> </p> <h4>Problem 1: authorization status is not available to apps</h4> <p> As of macOS Mojave beta 2, apps can't get their current Apple Event authorization status. In consequence: </p> <p> <ul> <li> <p><b>Apps can't time authorization</b></p> <p> Without information on its current authorization status, an app can't time when the user is asked for authorization. </p> <p> If the app knew that authorization has not yet been requested, it could, f.ex. bring up onboarding UI right after launch. </p> <p> Without that information, the app can't guard against the authorization prompt popping up at inappropriate or non-obvious times. </p> </li> <li> <p><b>Apps can't adapt their UI consistently</b></p> <p> Apps that offer additional features if authorized can't consistenly adapt their user interface without knowing their authorization status. </p> <p> Even if an app saved the previous result of sending an Apple Event to another app (and thereby the user's choice): how could it know about changes the user makes in System Preferences after that? </p> </li> <li> <p><b>Apps can't alert users to the lack of authorization</b></p> <p> Apps relying on the ability to control another app might want to provide inline information about the current authorization status. For example in their settings - or as part of built-in diagnostics. </p> <p> Right now, that information can't be retrieved without launching the target app and the risk of triggering an authorization prompt out of context. </p> </li> <li> <p><b>Apps can't avoid getting stuck indefinitely if there's no one to answer the authorization prompt</b></p> <p> Without knowing their current authorization status, apps can't guard against triggering an authorization prompt that blocks their sending thread indefinitely (this is bad). </p> <p> This can be particularly problematic for device management (if a script never finishes) and apps that provide users with accessibility or remote access to their Mac (if blocking the calling thread means to effectively lock out the user). </p> </li> </ul> </p> <p> We've had an <a href="https://developer.apple.com/documentation/applicationservices/1459186-axisprocesstrustedwithoptions" target="_blank">API to check for an app's accessibility status</a> since macOS 10.9 Mavericks. </p> <p> macOS Mojave <a href="https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624613-authorizationstatusformediatype">adds an API</a> for apps to get their current Camera and Microphone authorization status for pretty much the same reasons: timing, adapting UI, good user experience. </p> <p> <b>Possible solution:</b> <a href="#solution1">add a similar API for Apple Events</a>. </p> <h4>Problem 2: apps can't request approval without launching the target app</h4> <p> As of beta 2, apps can only prompt for authorization indirectly - by way of sending an Apple Event to the target app. This has several implications: </p> <p> <ul> <li> <p><b>The target app gets launched</b></p> <p> Sending an Apple Event makes macOS automatically launch the target app. This makes it impossible to prompt the user for approval for apps without launching them. </p> <p> Imagine what this makes the onboarding process look like for remote control <a href="https://www.iospirit.com/products/remotebuddy/" target="_blank">apps like mine</a> that target <a href="https://www.iospirit.com/products/remotebuddy/software/" target="_blank">100+ apps</a>: <ol> <li>every app the user wants to control would need to be launched at least once</li> <li>the authorization prompt needs to be read and answered for every app</li> <li>the target app needs to be quit</li> </ol> Launch, approve, quit - up to 100+ times. I don't see many new users willing to sit through this just to try out the app. </p> </li> <li> <p><b>There's no way to re-request authorization</b></p> <p> Think of a feature that involves controlling another app through Apple Events, for which the user has previously declined authorization. Possibly in error, or because it was requested in a different context - and the user didn't see the point. </p> <p> Whatever the reason, the user now wants to use the feature and clicks its button. </p> <p> The app gets to work, but sending the Apple Event to the target app fails with <code>errAEEventNotPermitted</code>. The app brings up an alert informing the user that the feature is not available because the app hasn't been authorized. </p> <p> How cool would it be for the user if he could now just click the "Authorize" button in the alert - and quickly go through the authorization prompt again? Compared to easy to miss instructions on which checkbox needs to be checked in System Preferences - and how to get to it. </p> </li> </ul> </p> <p> <b>Possible solution:</b> <a href="#solution2">add an API to request authorization</a> that is repeatable and doesn't require launching the app (similar to <a href="https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624584-requestaccessformediatype" target="_blank">what was added</a> to AVFoundation to request authorization for accessing camera and microphone</a> - or <a href="https://developer.apple.com/documentation/applicationservices/1459186-axisprocesstrustedwithoptions" target="_blank">what has been available for accessibility</a> since macOS 10.9 Mavericks). </p> <h4>Problem 3: No "allow all" option to whitelist apps</h4> <p> What about apps that control a large number of other applications - or whose list of target apps is open-ended? </p> <p> As of Beta 2, there's no adequate support for them. In order to get them fully working on macOS Mojave, users would need to get prompted for and authorize every targeted app individually. </p> <p> But even if an app could/would do that as part of an on-boarding step, users would have to be prompted <i>again</i> for every targeted app that's installed thereafter. </p> <p> Apple currently uses a private entitlement (<code>kTCCServiceAppleEvents</code>) to exempt <i>Script Editor</i> and <i>Automator</i> from Apple Event sandboxing, so users of these Apple apps will never see an authorization prompt. </p> <p> Making this entitlement available to <i>developers</i> would of course defeat the whole purpose of Apple Event sandboxing. So that's not an option. </p> <p> <i>Users</i>, however, should be able to pre-approve apps they trust, so these can freely send Apple Events to any other app - without bringing up prompts. </p> <p> For access to protected user files, macOS Mojave already provides this capability. Seemingly at least in part to avoid bringing up multiple approval prompts. From session 702: </p> <p style="text-align: center;"> <img src="/blog/2018/06/images/MojavePreApproval~8f2e69f5c8c8afc0fdd366e86eca8c6a1bf3495e.jpg" class="responsive"> </p> <p> <blockquote> <p> Apps that traverse the user's home folder could trigger multiple approval prompts, not just for Photos, Contacts, Calendars and so on. [..] So users can pre-approve such apps by adding them to the new "Application Data" category in the System Preferences Security &amp; Privacy pane. </p> </blockquote> </p> <p> <b>Possible solution:</b> provide a similiar <a href="#solution3">option in the Security &amp; Privacy pane, for pre-approving apps</a> to send Apple Events without limitations. </p> <h4>Problem 4: No support for usage descriptions</h4> <p> When prompting for access to Photos, Contacts, Calendars, etc., macOS Mojave allows apps to add purpose text to that prompt. From session 702: </p> <p style="text-align: center;"> <img src="/blog/2018/06/images/MojaveConsentText~e14b304f4a7bb8cd8098ee3e80f3a4747dd62240.jpg" class="responsive"> </p> <p> <blockquote> <p> When the user is prompted to authorize access to their personal data, it is important to communicate the purpose of that access. </p> <p> Imagine that you've just installed an app that you've never used before. And the very first time you launch it, you saw this prompt [without purpose text]. That's a tough decision. </p> <p> We can make that decision easier by including purpose text. </p> </blockquote> </p> <p> As of macOS Mojave Beta 2, Apple Event approval prompts don't support purpose text. </p> <p> Apple is spot on that communicating purpose is important - and helps users with tough decisions. So this is something that really should be supported when prompting for Apple Events as well. </p> <p> <b>Possible solution:</b> <a href="#solution5">add a <code>NSAppleEventUsageDescription</code> key to Info.plist</a> </p> <h4>Possible solutions</h4> <p> After talking about the rough edges and missing parts in the current implementation, here are my ideas for addressing them: </p> <a name="solution1"></a> <h5>API to query the current status</h5> <p> Inspired by <a href="https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624613-authorizationstatusformediatype">this AVFoundation API</a>, this is what the API providing authorization status for sending Apple Events could look like: </p> <p> <div class="code-box wraps">typedef NS_ENUM(NSUInteger, NSAppleEventAuthorizationStatus) { // User has not yet been prompted for permission. NSAppleEventAuthorizationStatusNotDetermined, // User has explicitely denied permission. NSAppleEventAuthorizationStatusDenied, // User has explicitely granted permission. NSAppleEventAuthorizationStatusAuthorized }; @interface NSAppleEventDescriptor (AuthorizationStatus) + (NSAppleEventAuthorizationStatus)authorizationStatusForTarget:(nullable NSAppleEventDescriptor *)eventDesc; @end </div> <p> Using an <code>NSAppleEventDescriptor</code> object as argument, this API could provide the authorization status for apps targeted by URL, bundle identifier or process ID (pid). </p> <p> Passing a <code>nil</code> value could request information on whether the app has been whitelisted to send Apple Events to any app without prompting. </p> <a name="solution2"></a> <h5>API to request authorization from the user</h5> <p> Inspired by <a href="https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624584-requestaccessformediatype">this AVFoundation API</a>, this is what the API to request prompting the user for authorization (without launching the target app) could look like: </p> <p> <div class="code-box wraps">typedef NS_OPTIONS(NSUInteger, NSAppleEventAuthorizationRequestOptions) { // No options NSAppleEventAuthorizationRequestOptionsNone, // Prompt the user - even if the user was already prompted in the past NSAppleEventAuthorizationRequestOptionsForcePrompt }; @interface NSAppleEventDescriptor (AuthorizationRequest) + (void)requestAccessForTarget:(nullable NSAppleEventDescriptor *)eventDesc options:(NSAppleEventAuthorizationRequestOptions)options completionHandler:(void (^)(BOOL granted))handler; @end </div> <p> Using an <code>NSAppleEventDescriptor</code> as argument, this API could bring up authorization prompts for apps identified by URL, bundle identifier or process ID (pid). </p> <p> If the target app described by the Apple Event Descriptor is not yet running, prompting for approval should not lead to launching this app. </p> <p> Passing a <code>nil</code> value for <code>eventDesc</code> could be a trigger to add the app to the "Automation" list (if it's not already in it) and bring up System Preferences with the Security &amp; Privacy pane open on the "Automation" category. This would be very useful for sending people to the right place to make changes. </p> <p> The <code>options</code> parameter could provide a way to pass additional options like prompting the user again, even if the user has previously been prompted for authorization. </p> <p> Finally, by using a completion handler, the user could be prompted for authorization without blocking the calling thread. </p> <a name="solution3"></a> <h5>System Preferences option to allow apps to control all other apps</h5> <p> Thinking about how an "allow all" or "whitelist" option could be integrated, I've come up with two ideas: </p> <p style="text-align: center;"> <a href="/blog/2018/06/images/MojaveImprovedPrefs~e3b233a4672eeccc901da3a93355a699a9fa9008.jpg" class="lightwindow"><img src="/blog/2018/06/images/MojaveImprovedPrefs~e3b233a4672eeccc901da3a93355a699a9fa9008.jpg" class="responsive"></a> </p> <section> <div class="row"> <div class="six columns"> <p> <b>Left:</b> adding a checkbox <i>"Allow automating all apps"</i> below each app. </p> <p> If the user selects it, it becomes the only checkbox shown for that app. The app is then pre-authorized to send Apple Events to any app. </p> <p> If the user unchecks it, the user is back to controlling authorization on a per-target basis. </p> </div> <div class="six columns"> <p> <b>Right:</b> adding a popup next to the app with two options "Ask" and "Allow all". </p> <p> <i>"Ask"</i> is selected by default and brings up a prompt for every target app. </p> <p> <i>"Allow all"</i> can be selected by the user to pre-authorize an app to send Apple Events to any other app. If that option is selected, the individual per-app checkboxes are no longer shown for the app. </p> </div> </div> </section> <a name="solution4"></a> <h5>Allow apps to opt-out of automatic prompts</h5> <p> Some apps could benefit of an option to opt out of automatic approval prompts from the OS altogether. This could possibly be expressed via a new Info.plist key (values: <code>automatic</code>, <code>never</code>): <div class="code-box wraps">&lt;key&gt;NSAppleEventAuthorizationMode&lt;/key&gt; &lt;string&gt;never&lt;/string&gt; </div> </p> <p> Apps that have opted out: <ul> <li>would get back <code>errAEEventNotPermitted</code> until the user has authorized the app to send Apple Events to the target app.</li> <li>would be responsible themselves for <a href="#solution2">requesting authorization</a>.</li> </ul> </p> <br> <a name="solution5"></a> <h5>Add support for Usage Description strings</h5> <p> In order to have meaningful and relevant purpose text for <i>every</i> prompt, it should be possible to provide per-app usage descriptions in addition to a default/fallback usage description: </p> <ul> <li><code>NSAppleEventUsageDescription</code> in Info.plist to specify a default/fallback string: <div class="code-box wraps">&lt;key&gt;NSAppleEventUsageDescription&lt;/key&gt; &lt;string&gt;Remote Buddy needs to send Apple Events to control this application.&lt;/string&gt; </div> </li> <li><code>NSAppleEventUsageDescriptionStringsFile</code> in Info.plist to point to a strings file that maps bundle identifiers to app-specific usage descriptions: <div class="code-box wraps">&lt;key&gt;NSAppleEventUsageDescriptionStringsFile&lt;/key&gt; &lt;string&gt;AppleEventUsageDescriptions&lt;/string&gt; </div> <p> And then, in <code>AppleEventUsageDescriptions.strings</code>: </p> <div class="code-box wraps">"com.apple.iTunes" = "Remote Buddy needs permission to display details on the currently playing song.";</div> </li> </ul> <h4>I need your help</h4> <p> I am deeply worried that the implementation of Apple Event sandboxing in Beta 2 could make it into the final release of macOS Mojave unchanged. </p> <p> As it is, it offers too little to developers who want to provide a good user experience. And not enough for utility apps and pro users who are in need of an option to exempt apps from Apple Event sandboxing. </p> <p> Right now there's a broad and diverse range of useful and beloved apps that take advantage of the Mac's support for automation. They make things "just work", help make the Mac even more accessible, increase productivity and make lifes easier and richer. </p> <p> For many, these apps are a reason to keep buying Macs - and a part of the Mac's heritage and DNA. </p> <p> Apple Events are the core technology making these apps possible. It is therefore essential to get right any changes to how Apple Events work. So that these apps can continue to exist and thrive. </p> <p> If you're using or making any of these apps, please help raise awareness at Apple on the importance of solving the problems presented here - by duping my radar (<a href="http://www.openradar.me/radar?id=5018965889777664" target="_blank">OpenRadar</a>, <a href="rdar://41570203">Radar</a>) and sharing this blog post. </p> <a name="update1"></a> <p> Thanks! ❤️ </p> <h4>Update 2018-07-03</h4> <p> Unlike stated above, sending an <i>Apple event</i> directly to an app (i.e. using <code>NSAppleEventDescriptor</code>) will not automatically launch the app. Instead <code>procNotFound</code> (-600) will be returned if the targeted app is not running. </p> <p> However, addressing a target app via AppleScript (like f.ex. <code>tell application "VLC" to play</code>) will launch the target app if it is not running. This is true even if the app executing the AppleScript isn't authorized to send Apple events to the target app. That the target app is launched in that case is a convenience afforded by AppleScript, however - and not a result of AppleScript's use of Apple events. </p> <p> That said, as of beta 2 of macOS Mojave, the target app still needs to be running for the sending app to get <code>errAEEventNotPermitted</code> in return, or the authorization prompt to pop up. So, as far as the effects in practice are concerned, the above still stands. It is, however, the sending app that needs to take care of launching the target app. "Raw" Apple events will not take care of this for the sending app. </p> <p> I apologize for the error. </p> <h4>Update 2018-08-30</h4> <p> I wrote a follow-up: <a href="/blog/2018/08/new-apple-event-apis-in-macos-mojave">macOS Mojave gets new APIs around AppleEvent sandboxing – but AEpocalypse still looms</a> </p> <a name="radar"></a> <hr> <div class="code-box wraps" style="background-color: #fafafa; border: none;"><h5>Bug #41570203 (<a href="http://www.openradar.me/radar?id=5018965889777664" target="_blank">OpenRadar</a>, <a href="rdar://41570203">Radar</a>)</h5> <b>Title:</b> The new Apple Event sandbox in macOS 10.14 Mojave lacks essential APIs <b>Product:</b> macOS + SDK / Foundation <b>Classification:</b> Bug | <b>Bug Type:</b> Serious Bug | <b>Reproducability:</b> Always <b>Description:</b> The Apple Event Sandbox implementation as of macOS 10.14 Mojave beta 2 (18A314h) lacks essential APIs that apps would need to adapt to and provide a good user experience around the change. -- I identified these issues with the current implementation: 1) timing: apps don't have sufficient control over when the user is prompted for authorization 2) adapting UI: apps have no guaranteed non-interactive/-blocking way to know if they're authorized to send AEs to another app to adapt their UIs 3) apps can't avoid getting stuck indefinitely if there's no one to answer the auth prompt (problem for remote/MDM/accessibility apps) 4) authorization requires launching the target app. Makes on-boarding for tools targeting many apps a UX nightmare. 5) even if trusting an app targeting a large (or even open-ended) list of other apps, users can't whitelist it to avoid constantly running into auth prompts. 6) authorization can't be re-requested. F.ex. in response to a click on an "Authorize" button in an alert stating that the app lacks a permission. 7) lack of context: users lack usage descriptions to inform their decision. -- What's missing: 1) an API to get the current authorization status without launching the target app, similiar to +[AVCaptureDevice authorizationStatusForMediaType:] or AXIsProcessTrustedWithOptions(). API idea: + (NSAppleEventAuthStatus)authorizationStatusForTarget:(nullable NSAppleEventDescriptor *)eventDesc; "eventDesc" identifies the target app. Passing nil returns the app's whitelist status (see pt. 3). The return value represents the current status: - not determined (user not prompted yet) - denied - authorized 2) an API to prompt without launching the target app, similar to +[AVCaptureDevice requestAccessForMediaType:completionHandler:] or AXIsProcessTrustedWithOptions(). It should be possible to bring up the prompt even if it has been brought up before. API idea: + (void)requestAccessForTarget:(nullable NSAppleEventDescriptor *)eventDesc options:(NSAppleEventAuthOptions)options completionHandler:(void (^)(BOOL granted))handler; "eventDesc" identifies the target app. Passing nil opens System Prefs showing "Automation". "options" allow to show the prompt even if it was shown before. "handler" is called with the result. 3) an option in System Prefs to "whitelist" apps, which can then send AEs to any other app (similar to what the new "Application Data" category achieves for file access). UI ideas: https://bit.ly/2yPoeaW 4) support default and app-specific usage descriptions in prompts. 5) a way for apps to opt out of automatic prompts (as triggered by sending an AE) altogether. Apps that opted out should be returned errAEEventNotPermitted, until the apps brought up the auth prompt (via the API from pt. 2) and won the user's approval. -- Due to length constraints, I couldn't include more examples and details on the suggested APIs in this radar. For these, please see my post on this topic: https://bit.ly/2KeNNaP </div> 2018-06-28T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2018/06/apple-event-sandboxing-in-macos-mojave Using letsencrypt with nginx to obtain, install and automatically renew SSL/TLS certificates <p>After not having been able to log into StartSSL’s interface for two days and the expiry of an important SSL/TLS certificate looming, I looked into letsencrypt again and found an easy way to make it work with nginx for all my domains. Here’s what I did:</p> <h4>Prepare nginx</h4> <p>To verify that you’re actually running the server behind www.example.com, letsencrypt needs the ability to put a file on your web server, so that the Letsencrypt CA can subsequently download it and establish your identity. These files all need to be reachable below <code>http://www.example.com/.well-known/acme-challenge/[cryptographic-filename]</code></p> <p>Instead of creating a bunch of new directories for this, I’m creating just one and then route traffic below <code>/.well-known</code> to that directory for all domains that I want to request a SSL/TLS certificate for:</p> <div class="code-box wraps">mkdir /etc/nginx/letsencrypt/webroot</div> <p>I then create an include file for my nginx configurations at <code>/etc/nginx/includes/letsencrypt.include</code> with this contents:</p> <div class="code-box wraps">location /.well-known {        default_type "/text/plain";        root /etc/nginx/letsencrypt/webroot; }</div> <p>Now, I can simply add</p> <div class="code-box wraps">include includes/letsencrypt.include;</div> <p>to the configuration of the servers I want to request a letsencrypt-certificate for:</p> <div class="code-box wraps">server {        listen 80;        server_name www.example.com, example.com;        include includes/letsencrypt.include;        # Your server specific configuration here }</div> <p>Finally, I tell nginx to reload the configuration:</p> <p><div class="code-box wraps">/etc/init.d/nginx reload</div></p> <h4>Install letsencrypt</h4> <p>This is pretty straightforward. Change to the directory you want to install letsencrypt in, then clone the git repository and change into the directory:</p> <div class="code-box wraps">git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt</div> <p>Now I can start requesting certificates using:</p> <div class="code-box wraps">./letsencrypt-auto certonly -a webroot --webroot-path=/etc/nginx/letsencrypt/webroot --rsa-key-size=4096 --agree-tos -d www.example.com -d example.com</div> <p>If everything works, you’ll now find private key, certificate and certificate chains in <code>/etc/letsencrypt/live/www.example.com/</code>.</p> <h4>Adding the certificate to your configuration </h4> <p>To use the letsencrypt certificate, point <code>ssl_certificate</code>, <code>ssl_trusted_certificate</code> and <code>ssl_certificate_key</code> to the respective files.</p> <p>Here’s a sample configuration for example.com with <a href="https://wiki.mozilla.org/Security/Server_Side_TLS">safe ciphers</a> and OCSP stapling:</p> <p><div class="code-box wraps">server { listen 443 ssl http2;       server_name www.example.com, example.com; ssl on; ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/chain.pem; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-S$ ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_tickets on; ssl_session_timeout 10m; # Your server specific configuration here }</div></p> <h4>Renew certificates in time</h4> <p>This is my favorite part about letsencrypt. Issued certificates expire 90 days after issuance, but the great folks at letsencrypt have made checking certificates for expiration and renewing them in time trivially simple:</p> <div class="code-box wraps">./letsencrypt-auto renew</div> <p>That’s all! No adding expiry reminders to the calendar, no issues with unavailable or slow web interfaces, no time wasted waiting for emails with verification codes. <i>Finally!</i> If you want to run this via cron, don’t forget to pair it with</p> <div class="code-box wraps">/etc/init.d/nginx reload</div> <p>so that nginx can pick up updated certificates.</p> <h4>Alternatives</h4> <p> If you can't or don't want to install software on your server, you can also obtain a letsencrypt certificate through <a href="https://gethttpsforfree.com/">this 3rd party web interface</a>. </p> 2016-04-17T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2016/04/using-letsencrypt-with-nginx-obtain-install-certificates 7 things I learnt creating an App Preview video for Apple TV <p>Between the initial release of <a href="https://www.iospirit.com/products/remotebuddy/display/">Remote Buddy Display</a> in November 2015 and the first update, Apple has added App Previews to the tvOS App Store: short videos, between 15 and 30 seconds in length, that allow potential new users to see an app in action before installing it.</p> <p>While I’m pretty happy with how the <a href="https://www.youtube.com/watch?v=PK_RNnRcR24">introductory video</a> for Remote Buddy Display turned out, it’s more than twice as long as the maximum permitted length for an App Preview video.</p> <p>So I accepted the challenge of the format and its constraints and jumped in. Here are a few things I learnt and thought about while creating the video:</p> <h4>1. App Previews on Apple TV are a big deal</h4> <p>First, literally, App Previews on tvOS are shown on what is probably the biggest screen in the home.</p> <p>Second, the way App Previews are integrated into the tvOS App Store differs greatly from the iOS App Store. If you add an App Preview to an iOS app, it’s poster frame dominates what people see of your app in search and on the app’s page in the App Store.</p> <p style="text-align: center;"> <img src="/blog/2016/04/images/ios-app-store-poster-frame~de0f2b041aa4473b21d997796d167fe088243839.jpg" class="responsive"> <small>Search result on the iOS App Store</small> </p> <p>On tvOS, App Previews are a much less risky proposition: the app’s icon continues to be shown in search — and the video does not occupy the place of the first screen shot.</p> <p>Instead, the poster frame of the App Preview now fills the right half of the page (blank otherwise) and a dedicated “Preview” button invites users to play the App Preview right away.</p> <p style="text-align: center;"> <img src="/blog/2016/04/images/app-store-listing~6e51d65897d09b1f806ae3e29e542d674264ec1b.png" class="responsive"> </p> <p>Third, the emphasis on paid content is stronger on the tvOS App Store: according to Apple data from early 2016, only 50% of tvOS apps can be downloaded for free (vs. 82% on iOS). That’s a good thing for indie developers, but I’d still expect users to hesitate before spending their dollars on an app they can’t try beforehand. A captivating App Preview, then, may be just what’s needed to convince them.</p> <p style="text-align: center;"> <img src="/blog/2016/04/images/app-store-catalog-by-business-model~ae30472153be64039e766fbe07cb292af529432a.png" class="responsive"> <small>iOS and tvOS App Store Catalogs by Business Model. Source: Apple TV Tech Talks</small> </p> <h4>2. Deciding on a storyboard & soundtrack early on helps immensely</h4> <p>When I do a new video for one of my apps, one of the first and most important things I do is to brainstorm what I want to show, bring these ideas into a logical order and — if possible — find a continuous stream of actions I can show.</p> <p>From this, I write a storyboard that contains all text titles, speech and scene contents in written form.</p> <p>Finally, I look for a soundtrack that fits the pace of the story I’m about to tell in my video.</p> <p>I found that writing a storyboard and deciding on a soundtrack early on really helps to keep production time to the minimum while making sure that the video I record fits the pace and special features of the background music.</p> <h4>3. Focus or die</h4> <p>At first, the 30 second limitation of App Previews gave me the horror. Now I appreciate it, because it rewards all the right things and discourages everything that might shy away new users.</p> <p>It forces me to focus. Focus on the best parts and meaningful interactions that best represent my app’s purpose.</p> <p>I simply don’t have the air time to show endless navigation through menu hierarchies. I need to get to the point. And fast. That’s a good thing.</p> <h4>4. Don’t layer text on top of video</h4> <p>In the past, I have often layered text on top of video to annotate and explain what is being shown. This can work well if what is shown takes long enough for the viewer to have a chance to both read the text and re-focus on and follow the action.</p> <p>In a 30 second video, however… forget it: the moment a viewer has completed reading the text, the action that’s been annotated has ended, too.</p> <p>So in the name of not letting anything happen concurrently in this video, I ended up alternating full screen text titles with video recorded from the Apple TV.</p> <p>The end result is just so much better.</p> <h4>5. Final Cut Pro X can animate Photoshop layers</h4> <p>When it comes to titling a video, my usual process is to create them with Keynote and export them as QuickTime file, which I then feed in Final Cut Pro X.</p> <p>This time, however, since I didn’t think I’d need any text transitions, I quickly threw some titles together in Photoshop and dropped the PSD files directly on Final Cut Pro X.</p> <p style="text-align: center;"> <img src="/blog/2016/04/images/final-cut-pro-x-photoshop-layers~1c632e060b481b1c0bd7312e2b79c2d2c27c3f4b.png" class="responsive"> </p> <p>Much to my surprise I found that Final Cut Pro X would turn every layer in the PSD file into a track of its own and allow me to apply transformations and transitions on them — just like with any other object.</p> <p>When I wanted one text transition later on, I could just create it right in Final Cut Pro X and skip Keynote entirely this time.</p> <h4>6. Keep the use of transitions and effects to a minimum</h4> <p>There is no shortage of wild, impressive transitions and effects in Final Cut Pro X and Keynote. Yet I have kept my use of transitions in the App Preview to a bare minimum (variations of simple cross fades, really).</p> <p>First, transitions eat time — and time is a particularly scarce resource in an App Preview video.</p> <p>Second, transitions and effects, if overused, can at best just be distracting — and at worst be damaging: after all, a beautiful frame around a bad photo doesn’t improve the photo. Instead it amplifies how bad the photo is.</p> <p>Or, put more positively™: if you have a great app, let is shine and speak for itself. Avoid ridiculing it through the overuse of transitions and effects.</p> <h4>7. The App Store adds a one-second transition at the beginning</h4> <p>Visiting Remote Buddy Display’s App Store page after the update went live, I noticed that the App Store had overlaid the first second with a transition: audio fades in slowly and the video fades in from black.</p> <p>As a result, the first full screen text title at the beginning of the video is only briefly visible, giving the video an abrupt start. It’s not a complete deal breaker, but I wish I had known of this beforehand.</p> <p>For the next update, I’ll account for this by inserting a second of silence and black at the beginning of the video.</p> <h4>Final thoughts</h4> <p>This is the first App Preview video I’ve made, and it will be interesting to see how and if it will affect the bottom line.</p> <p>However the result may look like, I’m already glad I made the video, because the process taught me some important lessons about making (hopefully) more impactful product videos.</p> <p>Here’s the result of roughly a day of work: <a href="https://www.youtube.com/watch?v=Mg5HZDbktHI">Remote Buddy Display: control your Mac with Apple TV and Siri Remote (App Preview video)</a> (YouTube)</p> 2016-04-05T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2016/04/7-things-I-learnt-creating-an-App-Preview-video-for-Apple-TV How to use the OS X 10.9 SDK with Xcode 7.3 <p>When Apple shipped Xcode 7, it decided to ship it only with the OS X 10.11 SDK. Unfortunately, this has since put developers of USB kernel extensions between a rock and a hard place.</p> <p>OS X 10.11 ships with a new USB stack and also took care of backward compatibility for both the kernel and user space APIs:</p> <blockquote> <b>OS X v10.11 Changes</b> <p>This article describes changes to USB in OS X v10.11.</p> <ul> <li><p>The USB stack is completely redesigned to increase stability and performance compared to Mac OS 10.10. Applications and third-party drivers for vendor-specific USB devices built with 10.10 SDK require no modification. </p></li><li><p>Applications that use IOUSBLib APIs should continue to work with no modification. IOKit drivers that use IOUSBDevice and IOUSBInterface APIs should continue to load and be functional with no regressions. </p></li><li><p>If you are developing a new IOKit driver that uses IOKIT USB APIs, please refer to the OS X v10.11 SDK for new API and classes such as IOUSBHostDevice and IOUSBHostInterface. New applications developed using the OS X v10.11 SDK should continue to use the IOUSBLib APIs.</p></li> <li><p>Different classes of USB devices should not exhibit any regression in functionality compared to the OS X v10.10 release.</p></li> </ul> </blockquote> <p><i>(Source: <a href="https://developer.apple.com/library/prerelease/mac/releasenotes/Darwin/RN_USB/Articles/10_11Changes.html#//apple_ref/doc/uid/TP40007739-CH3-SW1">Apple</a>, <a href="https://webcache.googleusercontent.com/search?q=cache:Ed1SaJyRUnQJ:https://developer.apple.com/library/prerelease/mac/releasenotes/Darwin/RN_USB/Articles/10_11Changes.html+&amp;cd=1&amp;hl=de&amp;ct=clnk&amp;gl=de">Google Cache</a>, <a href="https://twitter.com/felix_schwarz/status/661821443261538304">Tweet</a>)</i></p> <p>In essence, that means you can still write a USB kernel extension targeting older OS X releases and it will continue to just work™ under OS X 10.11, too.</p> <p>The OS X 10.11 SDK, however, lacks the needed header files for the old USB stack. And that’s why the OS X 10.9 SDK is still needed if you want to build these.</p> <p>Up until Xcode 7.2.1, this was as easy as copying the folder MacOSX10.9.sdk folder from Xcode 6.4 over into the Xcode 7.x application bundle.</p> <p>Not so in Xcode 7.3 – apparently because its MacOSX.platform contains a new key, specifically telling Xcode to ignore any SDKs prior to OS X 10.11.</p> <p><b>Here’s what I had to do to get Xcode 7.3 to work with the OS X 10.9 SDK:</b></p> <ol> <li><p>Quit Xcode 7.3.</p></li> <li><p>Copy</p><div class="code-box wraps">[Xcode6.4].app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk</div><p>to</p><div class="code-box wraps">/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/</div></li> <li><p>Open</p><div class="code-box wraps">/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Info.plist</div><p>and remove these two lines:</p><div class="code-box wraps"> &lt;key&gt;MinimumSDKVersion&lt;/key&gt;<br> &lt;string&gt;10.11&lt;/string&gt;</div></li> </ol> 2016-03-22T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2016/03/how-to-use-the-os-x-109-sdk-with-xcode-73 Symbolicating OS X panic logs <p> I was in need of symbolicating an OS X kernel panic.log and consulting Apple's <a href="https://developer.apple.com/library/mac/technotes/tn2063/_index.html">TN2063 (Understanding and Debugging Kernel Panics)</a> to do that, when I realized that Apple had last updated this document in 2008: kextload would complain about paramters it no longer knows about, gdb no longer ships with Xcode and the Kernel Debug Kits for more recent OS X releases neither include the referenced tools nor is their layout compatible with what that TechNote expects. </p> <p> In other words: if you want to symbolicate a recent kernel panic log, that TechNote - which used to be a great resource for that purpose - is no longer of any help; you're on your own. </p> <p> So, after some research, I'd like to share with you how I managed to symbolicate an OS X 10.11 panic log by hand using lldb and the kernel debug kit for 10.11: </p> <ol> <li>If you haven't already done so, download the kernel debug kit for the OS X release the panic occured on from <a href="https://developer.apple.com/downloads/">https://developer.apple.com/downloads/</a> and install it. That will add a kernel debug kit for that OS X release to /Library/Developer/KDKs/.</li> <li>Open Terminal.app and start an interactive lldb session with the kernel image of the KDK you just installed (all in one line): <div class="code-box">$ lldb /Library/Developer/KDKs/KDK_10.11_15A284.kdk/System/Library/Kernels/kernel</div> </li> <li>LLDB will inform you that 'kernel' contains a debug script and provides instructions to add these to the current session. Add them to the session. <div class="code-box wraps">(lldb) target create "/Library/Developer/KDKs/KDK_10.11_15A284.kdk/System/Library/Kernels/kernel" warning: 'kernel' contains a debug script. To run this script in this debug session: command script import "/Library/Developer/KDKs/KDK_10.11_15A284.kdk/System/Library/Kernels/kernel.dSYM/Contents/Resources/DWARF/../Python/kernel.py" To run all discovered debug scripts in this session: settings set target.load-script-from-symbol-file true Current executable set to '/Library/Developer/KDKs/KDK_10.11_15A284.kdk/System/Library/Kernels/kernel' (x86_64). (lldb) <strong>settings set target.load-script-from-symbol-file true</strong></div></li> <li>The kexts included in the backtrace are listed under "Kernel Extensions in backtrace" along with their addresses. Let's add them next, using this as a template: <div class="code-box wraps">(lldb) addkext -F <strong>[PathTo.Kext]</strong>/Contents/MacOS/<strong>[KextExecutable]</strong> <strong>[KextLoadAddress]</strong></div> The [KextLoadAddress] is the start address that is included after the @ sign. Example: <div class="code-box wraps">(lldb) addkext -F /Library/Extensions/My.kext/Contents/MacOS/My 0xffffff7f80d51000</div> </li> <li>We can now look up the symbol for any address by feeding the return address (on the right side of the colon) into <div class="code-box wraps">(lldb) image lookup -a <strong>[ReturnAddress]</strong></div> Example: <div class="code-box wraps">(lldb) image lookup -a 0xffffff7f80d536f5</div> </li> </ol> <p> I'm pretty sure there are better ways to use lldb to symbolicate a panic.log, but it's the only I could find for now. If you know about a more efficient way, please don't hesitate to post it in the comments. </p> 2015-10-05T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2015/10/symbolicating-os-x-panic-logs Calculating sunrise and sunset times without Core Location <p>I looked for a way to support day and night modes in an upcoming iOS app I’m working on. It is an integral part of the user experience, so ideally, it should “just work” - without requiring any steps on the part of the user.</p> <h4>Core Location</h4> <p>Core Location would prompt and require users to share their precise location (whereas an approximation would do). It also would need access to GPS and/or the Internet. That’s too many moving parts for it to “just work”.</p> <h4>GeoIP</h4> <p>I could put together a web service that tries to geo-locate users by their IP. However, I would not feel comfortable doing so without asking users for their consent first. It would also require an internet connection. In the end, it would be a lot like Core Location - just not as good. Which kind of makes this option ridiculous.</p> <h4>NSTimeZone</h4> <p>This may seem silly at first since places on the northern and southern hemisphere can share the same time zone, yet have very different sunrise and sunset times.</p> <p>Time zones on iOS, however, usually take the form of “Continent/City” (f.ex. “Europe/Berlin”) and not “GMT+1”. Plus, with default settings, iOS automatically selects and updates the correct time zone based on your actual location. Bingo!</p> <p>Unfortunately, while NSTimeZone provides the name of a timezone, it does not offer any location information.</p> <p>So I wrote a category to fill this gap. Based on data from the IANA Time Zone Database, <a href="https://github.com/iospirit/NSTimeZone-ISCLLocation">NSTimeZone+ISCLLocation</a> provides CLLocation and ISO 3166 country codes for location-based time zones under iOS and OS X.</p> <p>Here’s how it works:</p> <script class="github-gist" data-gdpr-consent-guard-src="https://gist.github.com/felix-schwarz/b58aa4d37eb3cee5de87.js"></script> <p>In my upcoming app, I'm using the returned location with <a href="https://github.com/BigZaphod/CLLocation-SunriseSunset">CLLocation-SunriseSunset</a> to calculate sunrise and sunset times. In cases where a location is not available, I use Core Location as a backup solution.</p> <p>You can find <a href="https://github.com/iospirit/NSTimeZone-ISCLLocation">the NSTimeZone+CLLocation repository on GitHub</a>.</p> <p>Enjoy!</p> 2015-03-31T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2015/03/calculating-sunrise-sunset-without-core-location Resolving symbols in a sample log from Activity Monitor <p>Often bugs don't crash an application right away but merely lead to it becoming unresponsive or "hanging".</p> <p>When I'm approached with such issues in my software, the first thing I ask for is a sample log obtained through Activity Monitor.</p> <p>Because the public builds of my software don't contain any debug symbols, I only see <em>??? </em>and a code offset for those spots that reference my code.</p> <p>There are <a href="http://www.acqualia.com/other/developer-tools" target="_blank">tools</a> and <a href="http://www.hockeyapp.net/" target="_blank">services</a> out there to symbolicate crash reports, but I'm not aware of anything such for sample logs, which have a different format.</p> <p>So here's what I do to resolve symbols in sample logs that I receive.</p> <p>Let's start with a sample log:</p> <div class="code-box wraps">Sampling process 3812 for 3 seconds with 1 millisecond of run time between samples Sampling completed, processing symbols... Analysis of sampling Remote Buddy (pid 3812) every 1 millisecond Process: Remote Buddy [3812] Path: /Applications/Remote Buddy.app/Contents/MacOS/Remote Buddy <strong>Load Address: 0x1000</strong> Identifier: com.iospirit.RemoteBuddy Version: 1.25 (1.25) <strong>Code Type: X86</strong> [..] <strong>Call graph:</strong> 2568 Thread_3519212 DispatchQueue_1: com.apple.main-thread (serial) + 2568 ??? (in Remote Buddy) load address 0x1000 + 0x1315 [<strong>0x2315</strong>] + 2568 ??? (in Remote Buddy) load address 0x1000 + 0x13ee [<strong>0x23ee</strong>] + 2568 ??? (in Remote Buddy) load address 0x1000 + 0x1548 [<strong>0x2548</strong>] [..] Binary Images: <strong>0x1000</strong> - 0x1edfe7 +com.iospirit.RemoteBuddy (1.25 - 1.25) &lt;<strong>B7BE5172-CEBA-3128-3805-4F81A1FCCA21</strong>&gt; /Applications/Remote Buddy.app/Contents/MacOS/Remote Buddy </div> <p>To resolve the symbols, I need the dSYM that was created alongside the build this sample was taken from. Each dSYM and app share a unique UUID. Here, it's <em>B7BE5172-CEBA-3128-3805-4F81A1FCCA21</em>.</p> <p>To verify that you're using the correct dSYM, you can print the UUID of your dSYM using <em>dwarfdump --uuid</em> on the DWARF file in your dSYM:</p> <div class="code-box wraps">xcrun dwarfdump --uuid "Remote Buddy.app.dSYM/Contents/Resources/DWARF/Remote Buddy"</div> <p>If your dSYMs were indexed by Spotlight, you can also search for the UUID in Spotlight to find the dSYM corresponding to the sample log you received.</p> <p>Using the <em>Code Type</em>, the <em>Load Address</em> and the code offsets in the <em>Call graph</em>, I can now take to the Terminal to resolve the numbers into symbols.</p> <p>For a <em>Code Type</em> of <em>x86</em>, the <em>Architecture</em> is <em>i386</em>, for a <em>Code Type</em> of X<em>86-64, </em>the architecture is<em> x86_64</em>.</p> <p>To open an interactive prompt, I type</p> <div class="code-box wraps">atos -arch [Architecture] -o [DWARF file] -l [Load Address]</div> <p>or</p> <div class="code-box wraps">atos -arch i386 -o "Remote Buddy.app.dSYM/Contents/Resources/DWARF/Remote Buddy" -l 0x1000</div> <p>in this example. I can then enter code offsets to resolve them into symbols:</p> <div class="code-box wraps">felix $<strong> atos -arch i386 -o "Remote Buddy.app.dSYM/Contents/Resources/DWARF/Remote Buddy" -l 0x1000</strong> got symbolicator for Remote Buddy.app.dSYM/Contents/Resources/DWARF/Remote Buddy, base address 1000 <strong>0x2315</strong> start (in Remote Buddy) + 41 <strong>0x23ee</strong> _start (in Remote Buddy) + 216 <strong>0x2548</strong> main (in Remote Buddy) (main.m:79) </div> <p>Press Ctrl+C to exit the interactive prompt.</p> <p>And that's it!</p> 2014-08-15T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2014/08/resolving-symbols-in-activity-monitor-sample-log How to build on 10.8 and earlier, then sign for and submit to the Mac App Store on 10.9 <p>Two days ago, Apple updated its <a href="https://developer.apple.com/library/prerelease/mac/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG205" target="_blank">Technote 2206</a> with a new paragraph on <a href="https://developer.apple.com/library/prerelease/mac/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG205" target="_blank">changes in OS X 10.9.5 and Yosemite DP5</a>:</p> <blockquote> Beginning with OS X version 10.9.5, there will be changes in how OS X recognizes signed apps. Version 1 signatures created with OS X versions prior to Mavericks will no longer be recognized by Gatekeeper and are considered obsolete. </blockquote> <p>Apple further emphasizes:</p> <blockquote> Important: For your apps to run on updated versions of OS X they must be signed on OS X version 10.9 or later and thus have a version 2 signature. </blockquote> <p>Apple also suggests what you can do if you build on older versions of OS X and distribute outside the Mac App Store:</p> <blockquote> If your team is using an older version of OS X to build your code, re-sign your app using OS X version 10.9 or later using the <code>codesign</code> tool to create version 2 signatures. Apps signed with version 2 signatures will work on older versions of OS X. </blockquote> <p>Pretty straightforward. Now what about apps on the Mac App Store?</p> <blockquote> If your app is on the Mac App Store, submit your re-signed app as an update. </blockquote> <p>Does that mean that Mac App Store apps submitted with a v1 signature will stop working on 10.9.5 and later?</p> <p>I quickly ran some tests:</p> <blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/danielpunkass?ref_src=twsrc%5Etfw">@danielpunkass</a> Just tested with a MAS application built and submitted on OS X 10.6.8: installed &amp; launched w/o problems. Non-MAS/GK: failed.</p>&mdash; Felix Schwarz (@felix_schwarz) <a href="https://twitter.com/felix_schwarz/status/496380618738245632?ref_src=twsrc%5Etfw">August 4, 2014</a></blockquote> <p>While this works now, it's not clear to me whether apps submitted with a v1 code signature will continue to be available on the Mac App Store and continue to work fine with OS X 10.9.5 and later.</p> <p>I prefer to be safe, not sorry, so I stick to what the Technote says (or seems to say), which is to submit an update with a v2 code signature.</p> <p>From the Technote, it's not really obvious how to do this for apps that have to be built on OS X 10.8 and earlier. Apple makes it clear that trying to use a copy of Mavericks' version of codesign on your build machine won't solve this:</p> <blockquote> The actual code signing machinery is part of the operating system, not the <code>codesign</code> tool. It will not work to copy the <code>codesign</code> tool from Mavericks to an older OS X version. </blockquote> <p>Currently I have to build parts of <a href="http://www.iospirit.com/products/remotebuddy/" title="Remote Buddy" target="_blank">Remote Buddy</a> in Xcode 3 running on OS X 10.6.8.</p> <p>Up to this date, this has never been a problem since I could still submit updates <a href="http://www.iospirit.com/products/remotebuddyexpress/" target="_blank">to the special Mac App Store version</a> right from within Xcode 3.</p> <p>The only way to obtain a v2 signature is by code signing under 10.9, but since Xcode 3 doesn't run on anything newer than 10.6.8, I'll have to separate the build process from the signing, packaging and submission process.</p> <p>Here's what I'm doing now:</p> <ol> <li>Build Remote Buddy Express on OS X 10.6.8 as usual.</li> <li>Copy the built app to a Mac running OS X 10.9.</li> <li>Run a script (SignAndPackageForMAS.sh, more on that later) that signs the app and packages it as a signed pkg file ready for submission to the Mac App Store.</li> <li>Use Application Loader (Xcode &gt; Open Developer Tool &gt; Application Loader) to submit the package (pkg) file to the Mac App Store.</li> </ol> <p>A copy of SignAndPackageForMAS.sh that you can adjust to your needs can be found at the end of this post or directly in <a href="https://gist.github.com/felix-schwarz/f8c83a6ebef0a7bb9dfa">this gist</a>. For most apps, you should only have to change the names of your signing identities and paths in the <em>Configuration</em> section to match your own setup.</p> <p>What the script does is to search for Cocoa bundle and frameworks inside your application and sign these before signing the application itself. It then uses <em>productbuild</em> to create a signed package ready for submission to the Mac App Store.</p> <p>If your app contains other types of binaries, be sure to sign these before signing the app bundle itself, starting with the binaries located at the deepest filesystem level, working up your way to the highest filesystem level.</p> <p><strong>Update:</strong> Thanks to <a href="https://twitter.com/felix_schwarz/status/497021842063097856" target="_blank">feedback</a> from Daniel Jalkut (<a href="https://twitter.com/danielpunkass" target="_blank">@danielpunkass</a>), I moved the codesign options to its own variable and added some information on --preserve-metadata.</p> <script class="github-gist" data-gdpr-consent-guard-src="https://gist.github.com/felix-schwarz/f8c83a6ebef0a7bb9dfa.js"></script> 2014-08-06T00:00:00Z Felix Schwarz https://www.felix-schwarz.org/blog/2014/08/build-on-108-and-earlier-then-sign-for-mas