Transfer Text In And Out Of The iOS Simulator Using xcrun

In a previous article, I covered creating, starting, and stopping iOS simulations. macOS comes with a handy tool to interact with the clipboard (aka pasteboard) on a Mac called pbcopy. You can redirect information from a file into your clipboard using the pbcopy command. Here, we’ll simply call pbcopy and then a file path

pbcopy ~/Desktop/transfer.txt

You can then redirect your text into simctl by doing a pbpaste into

xcrun simctl pbpaste booted

Once you’ve copied your data, clean up the transfer file:

rm ~/Desktop/transfer.txt

You can also pull text out. If you write data into the clipboard (e.g. during instrumentation) then you can extract it from that pasteboard using the simctl subcommand pbcopy as follows:

xcrun simctl pbcopy booted

The xcrun simctl subcommand also comes with a number of other pretty cool automations for programatic control, which I’ll try and cover later.

C++ Application To Write Data Into HKEY_CURRENT_USER

https://github.com/krypted/C-RegistryWriter – This project includes a RegistryKeyManager class that takes care of writing/reading to a registry file. Using a dedicated class makes expanding your registry functionality in the future easier.A DWORD is 4 bytes, so the value for RegistryWriter to send is currently limited to 4 characters (i.e. test1234 is not technically possible with a DWORD).

To use it, simply call the RegistryKeyManager class along with the key to write to and the value to put into the key:

RegistryKeyManager r( "HKEY_CURRENT_USER/Software/x/y", 1234 ); Which would create a key in HKEY_CURRENT_USER/Software/x/y with a DWORD value of 1234.

You could then read the value of and modify the key with calls like:

DWORD currentValue = r.ReadDwordValue(); r.WriteValue( 1234 );

A small helper method is also included in case you’d like to encode strings in the dword (since in your example you have used an ASCII string as the value. The method is RegistryKeyManager::ToDword(). You just need to call it around the value:

r.WriteValue( RegistryKeyManager::ToDword( "TEST" )); // Stores the bytes T, E, S, T in the DWORD value.

Controlling Multiple launchagents and launchdaemons concurrently

Most of my examples for launchctl have been per-user, per-agent, per-daemon. But you can also control multiple launchctl targets concurrently. One example would be that you can unload everything in the user domain by not specifying a path but providing the userid. In the following example, we’ll just use $userid as a variable, but it’s worth noting that that would be, as an example, 501 for the :

sudo launchctl bootout gui/$userid

There’s another option that can be used to do the opposite from within single user mode, called bootshell. Bootshell is called similarly from single user mode:

sudo launchctl bootshell

Obtain a bearer token using curl

In the following, we set a variable called BearerToken using a simple curl to the contents of a bearer token. We do so by running a curl with data in the header for “userid” although sometimes we see this as just “user” or “username” and then a password. This hits an endpoint called authenticationendpoint although sometimes we see that called “auth” or “authenticate” – in this specific case we’re pulling the bearer token out of “id” and it’s nested in there with a name of “token”:
BearerToken=$(curl -s -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"userid”:”{userid}”,”password":"{password}"}' https://www.krypted.com/api/authenticationendpoint | sed -E 's/\},\s*\{/\},\n\{/g' File | grep  ‘”id” : “token”’)
Once we have that token we can then pass it into another API via the Authorization header when connecting. In this example we’ll just pass the BearerToken we just captured as such, to an endpoint called EndpointName to www.krypted.com/api/EndpointName:
curl -H 'Accept: application/json' -H "Authorization: Bearer ${BearerToken}” https://www.krypted.com/api/EndpointName

C++ Program To Decode A JWT

Just posted a visual studio 2017 project at https://github.com/krypted/jwttools that includes an .exe file that, when run directly, extracts json out of an encoded jwt (and checks the signature). The encodedString variable in the jwt.cpp is a jwt that is used for extraction:

£include "pch.h" £include £include £include "jwt/jwt.hpp" int main() ¨ using namespace jwt::params; std::string encodedString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImY4ZWQ0OTM2LTBlN2ItNDlmYi05YTkxLWM2MDQyZDEzODMzNiIsImlhdCI6MTU1NTUxODk4NSwiZXhwIjoxNTU1NTIyNTg1fQ.rsyE_KzLO99zKXC7TNFD1RkU6HYF2Y7XJuIkFKumt20"; jwt::jwt_object decoded = jwt::decode( encodedString, algorithms(¨ "hs256" ¼), false ); std::cout << decoded.header(); std::cout << decoded.payload(); return 0;

The returned json output is as follows (e.g. run as c:/mydirectory/jwt.exe):

{"alg":"HS256","typ":"JWT"}{"admin":true,"exp":1555522585,"iat":1555522585,"jti":"f8ed4936-0e7b-49fb-9a91-c604d138336","name":"John Doe","sub":"1234567890"}

There's a lot more to it that I'll take a stab at documenting at a later date. Visual Studio 2017 project as well as executables included in the project.

Linear Algebra in Python Scripts

Preparing data for tensorflow is often easiest when done as a two step process. In machine learning, you often get into trying to plot points, calculate tangents, and a lot of basic algebra. Working out equations kinda’ reminds me of being in in-school suspension in high school. Except now we’re writing code to solve the problems rather than solving them ourselves.

I never liked solving for a matrix… But NumPy is a great little framework to import that does a lot of N-dimensional array work. A few basic tasks in the following script includes a number of functions across norms, matrix products, vector products, decompose, and eigenvalues. Remove/comment what you don’t need:

import numpy as np from numpy import linalg as LA array = [[-1,4,2],[1,-1,3]] array2 = [[6,2,1],[7,1,-3]] array = np.asarray(array) converted = np.fliplr(array) #ifsquare >> cholesky = LA.cholesky(array) #ifsquare >> inv = LA.inv(array) #ifsquare >> determinant = LA.det(array) #ifsquare >> signlog = LA.slogdet(array) print print 'ONE ARRAY' print 'Sum: ', np.trace(converted) print 'Elements: ', np.diagonal(converted) print 'Solved: ', LA.norm(array) print 'qr factorization: ' print np.linalg.qr(array) print 'Hermitian: ' print LA.svd(array) print print 'TWO ARRAYS' print 'vdot: ',np.vdot(array,array2) print 'Inner: ' print np.inner(array,array2) print 'Outer: ' print np.outer(array,array2) print 'Tensor dot product: ',np.tensordot(array,array2) print 'Kronecker product: ' print np.kron(array,array2)

Managing The Xcode Simulator Programmatically

The iOS Simulator is a great way to test watchOS, tvOS, and iOS apps while you’re writing them. The easiest way to work with the simulator is through Xcode. But you can also use simctl for interacting with it, helpful in automating QA operations when possible. The simctl binary is located at /Applications/Xcode.app/Contents/Developer/usr/bin/simctl and typically accessed as a verb from the /usr/bin/xcrun command. 

First let’s list all the simulators, done using the list command, called by simply running xcrun followed by simctl for the type of operation to be run and then the list command:

/usr/bin/xcrun simctl list

The output shows a lot of device types, runtimes, and devices, most of which should show as disconnected or shutdown:

== Device Types ==

== Device Types ==
iPhone 4s (com.apple.CoreSimulator.SimDeviceType.iPhone-4s)
iPhone 5 (com.apple.CoreSimulator.SimDeviceType.iPhone-5)
iPhone 5s (com.apple.CoreSimulator.SimDeviceType.iPhone-5s)
iPhone 6 (com.apple.CoreSimulator.SimDeviceType.iPhone-6)
iPhone 6 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus)
iPhone 6s (com.apple.CoreSimulator.SimDeviceType.iPhone-6s)
iPhone 6s Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus)
iPhone 7 (com.apple.CoreSimulator.SimDeviceType.iPhone-7)
iPhone 7 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-7-Plus)
iPhone 8 (com.apple.CoreSimulator.SimDeviceType.iPhone-8)
iPhone 8 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-8-Plus)
iPhone SE (com.apple.CoreSimulator.SimDeviceType.iPhone-SE)
iPhone X (com.apple.CoreSimulator.SimDeviceType.iPhone-X)
iPhone Xs (com.apple.CoreSimulator.SimDeviceType.iPhone-XS)
iPhone Xs Max (com.apple.CoreSimulator.SimDeviceType.iPhone-XS-Max)
iPhone Xʀ (com.apple.CoreSimulator.SimDeviceType.iPhone-XR)
iPad mini (5th generation) (com.apple.CoreSimulator.SimDeviceType.iPad-mini–5th-generation-)
iPad Air (3rd generation) (com.apple.CoreSimulator.SimDeviceType.iPad-Air–3rd-generation-)
iPad 2 (com.apple.CoreSimulator.SimDeviceType.iPad-2)
iPad Retina (com.apple.CoreSimulator.SimDeviceType.iPad-Retina)
iPad Air (com.apple.CoreSimulator.SimDeviceType.iPad-Air)
iPad mini 2 (com.apple.CoreSimulator.SimDeviceType.iPad-mini-2)
iPad mini 3 (com.apple.CoreSimulator.SimDeviceType.iPad-mini-3)
iPad mini 4 (com.apple.CoreSimulator.SimDeviceType.iPad-mini-4)
iPad Air 2 (com.apple.CoreSimulator.SimDeviceType.iPad-Air-2)
iPad (5th generation) (com.apple.CoreSimulator.SimDeviceType.iPad–5th-generation-)
iPad Pro (9.7-inch) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro–9-7-inch-)
iPad Pro (12.9-inch) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro)
iPad Pro (12.9-inch) (2nd generation) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro–12-9-inch—2nd-generation-)
iPad Pro (10.5-inch) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro–10-5-inch-)
iPad (6th generation) (com.apple.CoreSimulator.SimDeviceType.iPad–6th-generation-)
iPad Pro (11-inch) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro–11-inch-)
iPad Pro (12.9-inch) (3rd generation) (com.apple.CoreSimulator.SimDeviceType.iPad-Pro–12-9-inch—3rd-generation-)
Apple TV (com.apple.CoreSimulator.SimDeviceType.Apple-TV-1080p)
Apple TV 4K (com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-4K)
Apple TV 4K (at 1080p) (com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-1080p)
Apple Watch – 38mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-38mm)
Apple Watch – 42mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-42mm)
Apple Watch Series 2 – 38mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-2-38mm)
Apple Watch Series 2 – 42mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-2-42mm)
Apple Watch Series 3 – 38mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-3-38mm)
Apple Watch Series 3 – 42mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-3-42mm)
Apple Watch Series 4 – 40mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-4-40mm)
Apple Watch Series 4 – 44mm (com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-4-44mm)
== Runtimes ==
iOS 12.2 (12.2 – 16E226) – com.apple.CoreSimulator.SimRuntime.iOS-12-2 
tvOS 12.2 (12.2 – 16L225) – com.apple.CoreSimulator.SimRuntime.tvOS-12-2 
watchOS 5.2 (5.2 – 16T224) – com.apple.CoreSimulator.SimRuntime.watchOS-5-2 
== Devices ==
— iOS 12.2 —
    iPhone 5s (4A2CC670-55EF-4B36-896F-EC2009EDEC07) (Shutdown) 
    iPhone 6 (937105ED-AEF1-4487-B406-1795D9AD9243) (Shutdown) 
    iPhone 6 Plus (E4031984-D79E-4888-A104-0C6DA73EB67F) (Shutdown) 
    iPhone 6s (EA859DD2-D934-4CB1-B2D5-055F1DF93FB9) (Shutdown) 
    iPhone 6s Plus (CFFD27D8-4FEE-4632-B3BB-8133DBF74925) (Shutdown) 
    iPhone 7 (66BE2C4D-CB66-4618-A95D-BF239C569965) (Shutdown) 
    iPhone 7 Plus (3C6B97F3-AF19-49A9-B400-09A5CBCCB3D6) (Shutdown) 
    iPhone 8 (2D18B685-A642-44E5-BF8E-6D09AB478B0F) (Shutdown) 
    iPhone 8 Plus (BF652604-8F18-457C-B111-E951E8B55947) (Shutdown) 
    iPhone SE (384E1A51-7354-4FAC-A1A8-CB2B9CC5D1CF) (Shutdown) 
    iPhone X (98DD2AEC-4566-4896-83E5-E0B8603FFE9E) (Shutdown) 
    iPhone Xs (02A90E87-B2C4-44FE-9477-DD9455D7CB0C) (Shutdown) 
    iPhone Xs Max (79F42764-5020-41EA-87C3-55C541A790F5) (Shutdown) 
    iPhone Xʀ (AD6FD253-20A7-432C-8C22-5070489E787E) (Shutdown) 
    iPad Air (3rd generation) (DAB405D3-C1FD-47ED-B4FE-F0E1857A2862) (Shutdown) 
    iPad Air (3216E338-D522-43C7-9484-D7AEA081E926) (Shutdown) 
    iPad Air 2 (6B0BCC75-0ED6-4FC9-94C5-48A3466B79AD) (Shutdown) 
    iPad (5th generation) (24313239-A853-46EB-B5A3-0DD8A86080F2) (Shutdown) 
    iPad Pro (9.7-inch) (8E2BEE13-D726-4CF5-8388-0913B87495F1) (Shutdown) 
    iPad Pro (12.9-inch) (9493C54B-D61E-4D42-8952-A89C2CCF2FDD) (Shutdown) 
    iPad Pro (12.9-inch) (2nd generation) (5D978189-856D-4598-8C09-876BD832C71F) (Shutdown) 
    iPad Pro (10.5-inch) (2BF88E18-8C89-4515-A486-DC72C7A8629E) (Shutdown) 
    iPad (6th generation) (016DAA95-D917-4CD9-9A54-700D01BAF1AE) (Shutdown) 
    iPad Pro (11-inch) (307BEEDD-8652-4438-944B-CB423A26CC9E) (Shutdown) 
    iPad Pro (12.9-inch) (3rd generation) (D893291C-F214-4B43-A54F-3AB95A5442ED) (Shutdown) 
— tvOS 12.2 —
    Apple TV (1562B2B3-F403-4C99-8026-20AA51C8716B) (Shutdown) 
    Apple TV 4K (6C5CC68C-4C01-4097-91B8-A503B465F961) (Shutdown) 
    Apple TV 4K (at 1080p) (B8DCEFC3-F5BC-455D-B8B7-BAE7A48BE032) (Shutdown) 
— watchOS 5.2 —
    Apple Watch Series 2 – 38mm (06938C62-9757-4F5E-913F-F9639D8AFF9C) (Shutdown) 
    Apple Watch Series 2 – 42mm (BBA6DFC3-BC5A-494F-AFF4-E2B1331AFC69) (Shutdown) 
    Apple Watch Series 3 – 38mm (2D70BA7B-66BF-43E1-9F80-20EEFF588BB5) (Shutdown) 
    Apple Watch Series 3 – 42mm (F0E4E11E-639C-4F45-974A-2AAC684F1411) (Shutdown) 
    Apple Watch Series 4 – 40mm (B0CC419F-220F-46E1-B827-B512B08552B6) (Shutdown) 
    Apple Watch Series 4 – 44mm (7DAE1296-C9FF-4759-A066-1BA42A259853) (Shutdown) 
== Device Pairs ==
620743A8-FD31-408C-AEBF-8D0F0849BD9D (active, disconnected)
    Watch: Apple Watch Series 4 – 40mm (B0CC419F-220F-46E1-B827-B512B08552B6) (Shutdown)
    Phone: iPhone Xs (02A90E87-B2C4-44FE-9477-DD9455D7CB0C) (Shutdown)
20E13C7F-3788-4DA5-962A-FF1389CDDA12 (active, disconnected)
    Watch: Apple Watch Series 4 – 44mm (7DAE1296-C9FF-4759-A066-1BA42A259853) (Shutdown)
    Phone: iPhone Xs Max (79F42764-5020-41EA-87C3-55C541A790F5) (Shutdown)

Next, let’s create a fresh new spiffy simulator called testing_iPhone7. To do so we’ll 

/usr/bin/xcrun simctl create testing_iPhone7 com.apple.CoreSimulator.SimDeviceType.iPhone-7 com.apple.CoreSimulator.SimRuntime.iOS-12-2

The output includes a UUID such as the following. That can then be used to track further interactions with the simulation:

E6D4C2B8-0601-4557-99DA-B6B8251D534D

The most common tasks would be booting, shutting down, erasing, and opening simulations. First let’s boot it up:

/usr/bin/xcrun simctl boot E6D4C2B8-0601-4557-99DA-B6B8251D534D

To shut that same simulator down use the shutdown verb:

/usr/bin/xcrun simctl shutdown E6D4C2B8-0601-4557-99DA-B6B8251D534D

Neither of these commands provide any output on success, but do error on failure. Once you’ve run tests, I like to erase my simulator and start fresh. To do so, simply use the erase command:

/usr/bin/xcrun simctl erase E6D4C2B8-0601-4557-99DA-B6B8251D534D

To open the simulator you loaded, you can use the open Simulator.app :

open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/

Simulator App

For more information on using simctl subcommands, use the help subcommand:

/usr/bin/xcrun simctl help

Notice there are a lot of verbs, which I’ll cover further in later articles:

create              Create a new device.

clone               Clone an existing device.
upgrade             Upgrade a device to a newer runtime.
delete              Delete a device or all unavailable devices.
pair                Create a new watch and phone pair.
unpair              Unpair a watch and phone pair.
pair_activate       Set a given pair as active.
erase               Erase a device’s contents and settings.
boot                Boot a device.
shutdown            Shutdown a device.
rename              Rename a device.
getenv              Print an environment variable from a running device.
openurl             Open a URL in a device.
addmedia            Add photos, live photos, videos, or contacts to the library of a device.
install             Install an app on a device.
uninstall           Uninstall an app from a device.
get_app_container   Print the path of the installed app’s container
launch              Launch an application by identifier on a device.
terminate           Terminate an application by identifier on a device.
spawn               Spawn a process by executing a given executable on a device.
list                List available devices, device types, runtimes, or device pairs.
icloud_sync         Trigger iCloud sync on a device.
pbsync              Sync the pasteboard content from one pasteboard to another.
pbcopy              Copy standard input onto the device pasteboard.
pbpaste             Print the contents of the device’s pasteboard to standard output.
help                Prints the usage for a given subcommand.
io                  Set up a device IO operation.
diagnose            Collect diagnostic information and logs.
logverbose          enable or disable verbose logging for a device

Next up, we’ll look at performing tasks in the Simulator in the next article on regression testing with the Simulator app.

Device Snapshots and Crafting A Restore Device User Experience for Macs

I recently worked on something where a design requirement was to build a good snapshot restore option but not to use Time Machine backups. You can capture a snapshot of a Mac without enabling Time Machine. To do so, you’d still use the same binary as you would with Time Machine, /usr/bin/tmutil. To do so, simply use the snapshot verb as follows:

/usr/bin/tmutil snapshot

Once you’ve run that, you get output similar to the following:

Created local snapshot with date: 2019-04-12-110248

Now you have a snapshot that can be used to restore a Mac using the steps shown in this article: https://maclovin.org/blog-native/2017/restoring-from-a-snapshot-with-apfs.

You can make a snapshot at the provisioning time of a Mac and then see that snapshot at any point by awking for the first line in a list of snapshots, unless it’s been deleted:

snapshot=`tmutil listlocalsnapshots / | awk -F "." '{print $4}' | head -1`

Now when it comes to making it simple for end user restores, there’s a few gaps. The first is that you have to boot with Command-R and can’t build a scripted restore option. So the following won’t work on a machine with SIP:

sudo nvram boot-args="-x"; restart

You can prune the list of snapshots so it’s simple for a user to pick the right one though. To see a list of all your snapshots for the current boot volume, use that listlocalsnapshots verb:

tmutil listlocalsnapshots /

This provides the output as com.apple.TimeMachine.2019-04-12-110248 and when deleting snapshots you don’t need the com.apple.TimeMachine. portion of that string, so to delete the last snapshot we’ll gather it into a variable with the following:

snapshot=`tmutil listlocalsnapshots / | awk -F "." '{print $4}' | tail -1`

Alternatively you can list snapshots with the diskutil command, using the apfs option and listSnapshots verb, followed by your boot volume as follows, but the output isn’t pretty:

diskutil apfs listSnapshots disk1s1

Once you’ve parsed the service name portion out, you can then delete a given snapshot using the deletelocalsnapshots verb with tmutil:

tmutil deletelocalsnapshots 2019-04-12-110248

Or to use that snapshot variable:

tmutil deletelocalsnapshots $snapshot

So I can present the user, when they try to revert, with a shorter list of snapshots, hopefully helping them to select the right one. But, I can only do a scripted restore if I’m actually using Time Machine and I have a volume taking those backups. If I’m doing a snapshot restore I have to restart the Mac holding down the command- R keys and then once booted in recovery mode, select the snapshot and wait for the restore.

Booting into recovery mode takes a long time so seems like something you’d teach the desktop techs to do rather than expect users to do a-la-“Erase all Contents and Settings” button. The whole process takes about 20 minutes because of how long it takes to boot into this state and revert.

It’s actually just as quick to do a full reinstallation. This can be as simple as using the startosinstall --eraseinstall command, which provides a number of options. It seems silly to load up a whole new operating system when you have one there, but assuming you have decent internet speeds, it’s just as fast to do so. There’s even a gui wrapper for it to make the whole process easier for your users at https://scriptingosx.com/2018/10/eraseinstall-application/ which could then be provided in tools like Managed Software Center and Self Service. You could also try and remove all user and app data using a script but that has turned out to be a lot of moving parts that requires constant updates.

If you were to opt into a snapshot-based restore option, it’s also worth noting that if you have outdated certificates, outdated passwords, outdated software, etc, that it can be problematic. So we’re pretty much right back to erase-install.

Other people have gone down this same rabbit hole:

  • https://grahamrpugh.com/2018/03/26/reinstall-macos-from-system-volume.html
  • https://www.jamf.com/jamf-nation/discussions/26716/summer-2018-wiping-student-systems

So my initial goal was to create an article on doing an elegant restore process for the Mac, where you script more of it and provide a better experience. But alas, I have failed ya’. Sorry. Do you have a better way? If so, please comment!

Adding App Notarization For Macs To Your Build Train

Apple sent the following message out to developers yesterday:

Dear Developer, 

We’re working with developers to create a safer Mac user experience through a process where all software, whether distributed on the App Store or outside of it, is signed or notarized by Apple. With the public release of macOS 10.14.5, we require that all developers creating a Developer ID certificate for the first time notarize their apps, and that all new and updated kernel extensions be notarized as well. This will help give users more confidence that the software they download and run, no matter where they get it from, is not malware by showing a more streamlined Gatekeeper interface. In addition, we’ve made the following enhancements to the notarization process.
Legacy code is fully supported, even if it contains unsigned binaries. While new software and updates require proper signatures in order to be notarized, you can upload your existing software as-is.Apps with plugin ecosystems are better supported.Stapler supports all types of bundles and plugins.Xcode 10.2 adds secure timestamps and other code signing options required by the notary service.Related documentation has also been improved. We encourage you to take look at Notarizing Your Apps Before Distribution and Hardened Runtime Entitlements.

If you have any questions, contact us

Best regards,
Apple Developer RelationsTM and © 2019 Apple Inc.
One Apple Park Way, MS 301-1TEV, Cupertino, CA 95014.

All Rights Reserved | Privacy Policy | Account 

If you would prefer not to receive future communications from Apple Developer, you may unsubscribe.

Many organizations have a solution to automate their build process for software and will need to now add submitting an app for notarization to this process. Before you start, there are a few things you should know:

  • This is an automated scan that usually takes about 20 minutes and requires at least the 10.9 macOS SDK.
  • Before submitting, make sure code-signing has been enabled for all executables and that you enabled the Hardened Runtime option.
  • Find a workaround if you’re setting com.apple.security.get-task-allow to true for any reason.
  • Make sure to use an Apple Developer ID instead of a local cert from Xcode for apps and kexts. And make sure all code-signing certs have a timestamp when running your distribution workflows in Xcode or if using codesign make sure to add –timestamp.

You can use any tools for the next steps. Because I have a Bamboo setup on my desk, next I’m going to open it and create a command task. To do so:

  • Open the Tasks configuration tab for a job (or default job in a new plan).
  • Click Add Task.
  • Add a Task Description, which is just how the task is described in the Bamboo interface.
  • Uncheck the box to “Disable this task”
  • Provide a path to the command executable, which in this case will be a simple bash script that we’ll call /usr/bambooscripts/notarize.sh. If you’re stringing workflows together you might add other scripts as well (e.g. a per-product script as opposed to a generic script that takes positional parameters for arguments).
  • Provide any necessary Arguments. In this case it’ll just be a simple job but you can reduce the work by adding arguments for processing paths of different products.
  • Provide any necessary Environment Variables. We won’t use any in this project.
  • Provide any necessary “Working Sub Directory” settings, which is an alternative directory rather than using a relative path. If you don’t provide a working sub directory, note that Bamboo looks for build files in the root directory.
  • Click the Save button (as you can see below).

Now we’ll need to use scrub with the altool. Here, we’ll use the –notarize-app option and then define the bundle (using the reverse naming convention you’ve always used for the –primary-bundle-id option and then the username and password from your Apple ID linked to your Developer ID and finally the –file which is the zipped output from Xcode.

#!/bin/bash /usr/bin/xcrun/xcrun altool --notarize-app --primary-bundle-id "com.myorg.myproduct” --username “krypted@myorg.com” --password “icky_passwords“ --file "/Users/krypted/Documents/myproduct.zip"

We'll call this script /usr/bambooscripts/notarize.sh and then let the job pick it up and process it.

Oh funny. I just noticed Rich Trouton posted a writeup on Notarization at https://derflounder.wordpress.com/2019/04/10/notarizing-automator-applications/. I'd read that as well.