Indie Notes 2018-07-03T21:58:38Z Apple Event sandboxing in macOS Mojave lacks essential APIs <p> In the WWDC 2018 session <a href="" 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></i> sends an Apple Event to <i></i>: </p> <p> <ul style="margin-left: 2rem;"> <li>the <i></i> thread sending the Apple Event is blocked</li> <li><i></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></i> for this and other Apple Events sent to <i></i> in the future.</li> <li><b>OK</b>: the thread resumes execution. This - and future Apple Events from <i></i> to <i></i> - are delivered without prompting the user again.</li> </ul> </p> <p> Noteworthy: </p> <p> <ul style="margin-left: 2rem;"> <li><b><i></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="" target="_blank">API to check for an app's accessibility status</a> since macOS 10.9 Mavericks. </p> <p> macOS Mojave <a href="">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="" target="_blank">apps like mine</a> that target <a href="" 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="" target="_blank">what was added</a> to AVFoundation to request authorization for accessing camera and microphone</a> - or <a href="" 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="">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="">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">"" = "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="" 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> <a name="radar"></a> <hr> <div class="code-box wraps" style="background-color: #fafafa; border: none;"><h5>Bug #41570203 (<a href="" 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: 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: </div> 2018-06-28T00:00:00Z Felix Schwarz 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, 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>[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,;        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 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 -d</div> <p>If everything works, you’ll now find private key, certificate and certificate chains in <code>/etc/letsencrypt/live/</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 with <a href="">safe ciphers</a> and OCSP stapling:</p> <p><div class="code-box wraps">server { listen 443 ssl http2;       server_name,; ssl on; ssl_certificate /etc/letsencrypt/live/; ssl_certificate_key /etc/letsencrypt/live/; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/letsencrypt/live/; 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="">this 3rd party web interface</a>. </p> 2016-04-17T00:00:00Z Felix Schwarz 7 things I learnt creating an App Preview video for Apple TV <p>Between the initial release of <a href="">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="">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="">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 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="">Apple</a>, <a href=";cd=1&amp;hl=de&amp;ct=clnk&amp;gl=de">Google Cache</a>, <a href="">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/</div></li> <li><p>Open</p><div class="code-box wraps">/Applications/</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 Symbolicating OS X panic logs <p> I was in need of symbolicating an OS X kernel panic.log and consulting Apple's <a href="">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=""></a> and install it. That will add a kernel debug kit for that OS X release to /Library/Developer/KDKs/.</li> <li>Open 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/" 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 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="">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=""></script> <p>In my upcoming app, I'm using the returned location with <a href="">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="">the NSTimeZone+CLLocation repository on GitHub</a>.</p> <p>Enjoy!</p> 2015-03-31T00:00:00Z Felix Schwarz 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="" target="_blank">tools</a> and <a href="" 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 <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: (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 </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"</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" -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" -l 0x1000</strong> got symbolicator for 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 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="" target="_blank">Technote 2206</a> with a new paragraph on <a href="" 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="">@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="">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="" 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="" 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 (, 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 that you can adjust to your needs can be found at the end of this post or directly in <a href="">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="" target="_blank">feedback</a> from Daniel Jalkut (<a href="" 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=""></script> 2014-08-06T00:00:00Z Felix Schwarz