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
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:
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.
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
To open the simulator you loaded, you can use the open 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.
The Google Directory integration with GSuite allows you to manage which devices have access to GSuite. This allows you to control access based on a variety of factors.
Below, you’ll find a Google Cloud Function that is meant to respond to a webhook. This function takes an action to set a device into ‘approve’ or ‘deny’ as a state within Google Directory. Before using the function you’ll want to set CustomerID, ResourceID, and EMAIL_ACCOUNT for your GSuite account before using.
Once you have all that, you can upload mobiledevice.py in your Google Cloud Console.
# Google Cloud Function meant to respond to a webhook
# Takes an action to set a device into approve or deny state
# Set CustomerID, ResourceID, and EMAIL_ACCOUNT for your GSuite account before using
from google.oauth2 import service_account
The curl command can be used to authenticate to an API using a variety of authentication types such as Bearer, OAuth, Token, and of course Basic. To authenticate to the ZuluDesk API, first create an API token. This is done by logging into ZuluDesk, clicking Organization, then Settings, then API, an then clicking on the Add API Key button.
Once you have your API key, your header will look as follows:
The curl command can do this would be as follows, simply converting these into separate values in the -H or header. The URL provided will do a GET against devices, displaying a list of devices in json:
A webhook is a small web trigger that when fired can easily send amount of small json to a web listener. Most modern software solutions support webhooks. They provide an easy way to trigger events from a piece of software to happen in another piece of software.
An example of this is when a smart group change happens in Jamf Pro, do something elsewhere. To start, you register a webhook in Jamf Pro by opening an instance of Jamf Pro, clicking on Settings, clicking on Global Management, and then clicking on Webhooks.
From the Webhooks screen, click New.
At the New Webhook screen, you will see a number of fields. Here,
Display Name: The name used to identify the webhook in Jamf Pro.
Enabled: Check to enable the webhook, uncheck the box to disable the webhook.
Webhook URL: The URL that the json or xml will be sent to (note that you’ll need something at this URL to accept your webhook).
Authentication Type: None is used for an anonymous webhook and basic can be used to send a username and password to the webhook listener.
Connection Timeout: How long the webhook will attempt to open a connection before sending data.
Read Timeout: How long the webhook will attempt to send data for before it turns off.
Content Type: Choose to send information via xml or json.
Webhook Event: The type of event that Jamf Pro can send a hook based on.
Do you know how much time you spend in various apps and on your device? Do you want to gently be reminded of how much time you’re staring at screens and maybe even be limited in how much you can be lost on the screen?
First, let’s Let’s do this limiting the time you can be on the device in the first place, using a feature of Screen Time called Downtime:
Tap on Screen Time
Tap on and then set the start of Downtime and the stop of Downtime. Tap back on Screen Time in the upper left hand corner of the screen. Now, let’s setup an app limit for social apps (because really, most of us are on those wayyyy too much:
Tap on Screen Time
Tap App Limits
Tap an app category (e.g. Social Networking)
Set the number of hours you can use that type of app (note, if you set 23 hours and 59 minutes you are totally cheating)
Should you want to remove those limits you created, just tap Delete Limit. Or better, just configure apps that are allowed to bypass the limits you’ve made by tapping Always Allowed and adding apps that are always allowed to work. This allows you to limit all your apps except, as an example, Maps and Camera.
Another option in Screen Time is Content and Privacy Restrictions. To configure these:
Tap on Screen Time
Tap on Content & Privacy Restrictions
Turn Content & Privacy Restrictions on by tapping the slider
Tap on iTunes & App Store Purchases
Here, you can limit installing apps, deleting apps, or making in-app purchases on the device. You can also just force a password in order to make any purchase from iTunes, Book Store purchases, or App Store purchases
Tap the back button
Tap Allowed Apps
Use the indicator light to disable any app you don’t want to be able to access on this profile
Once all apps are configured, tap the back button
Tap Content Restrictions
There are a lot of restrictions available. Most are mirrored with a profile and so can be controlled by an MDM as well:
Country: Start with the country your ratings are set for.
Music, Podcasts, & News: Then, choose what whether or not explicit content is allowed (and by content we really mean music, podcasts, & news).
Music Profiles & Posts: Then choose whether the device is allowed to publish music options and posts about music.
Movies: Then set a maximum AFTRA rating (e.g. PG-13 or R) for content.
TV Shows: Select the TV ratings allowed (e.g. TV-G or TV-MA for mature audiences)
Books: Luckily, Tipper Gore never got her way so there’s no true rating systems for books. Just select Clean or Explicit.
Apps: Choose an age that ratings for apps are most appropriate
Web Content: Limit access only to specific websites, limit access to adult websites, or provide unrestricted access to web content
Web Search Content: Allow Siri to access the web to search
Explicit Language: Allow or restrict Siri from using dirty words
Multiplayer Games: Allow or deny access to multiplayer games
Adding Friends: Allow or deny access to add friends within the Game Center app
Screen Recording: Allow or deny access to screen recordings
Next, go back and in the privacy section, configure what apps are able to access Location Services, Contacts, Calendars, Reminders, Photos, Share My Location, Bluetooth Sharing, Microphone, Speech Recognition, Advertising, Media And Apple Music.
Finally, under allow changes, configure whether you’ll be able to make changes to Passcode Changes, Account Changes, Cellular Data Changes, Volume Limits, Do Not Disturb While Driving, TV Providers, and Background App Activities.
It’s been a great year for the MacAdmins Podcast. And a special thank you to Tom, Marcus, Emily, James, and the former co-host Pepijn for continuing to allow me to be a part of something special. The last episode of the year is available at podcast.macadmins.org, using the below embedded link, or wherever you get your podcasts!
Hey look, there’s a new category on the Jamf Marketplace, available at https://marketplace.jamf.com/apps/#category=AppConfig,selecting the AppConfig category. The new AppConfig category gives administrators of any MDM that supports AppConfig access to a set of apps that support AppConfig. If you have an app that isn’t listed here, feel free to let me know. What does this mean? Well, AppConfig is a way of sending data into an app. App config allows a customer to deploy settings into applications on iOS devices in much the same way that settings can be sent into a Mac app via the defaults command. This means an end user could get an app installed on their device from the iOS App Store, a custom app, or a B2B app and that app would have any settings the user might need to connect to servers or configure the experience.
So what is Managed App Config? At it’s most basic, you identify a label and a value in XML and send it to an iOS device that’s running iOS 7 or later (e.g. via Jamf 9 and up). The vendor who makes the app has to basically define what those settings are. Which brings up an interesting problem never fully addressed with defaults domains: standardization and ease-of-use (although MCX was close).
AppConfig.org is a consortium of MDM vendors and software vendors that maintain the emerging AppConfig standards around Managed App Config (within the confines of what Apple gives vendors) and then makes a feed of settings for apps that conform to those standards. Jamf is a founding member of Appconfig.org, along with MobileIron and AirWatch. Examples of what you could put into the AppConfig.org feed include
Managed App Config options are set by vendors at compile time within the code and then the XML sent with the app is parsed by the app at installation time. If you’re a software vendor who wants to get started with AppConfig, check out the Spec Creator from Jamf Research or get in touch with the developer relations team from any MDM vendor.
If you’re a customer of an app and would like to leverage Managed App Config and your vendor isn’t listed on the appconfig.org site, get in touch with them, as this is the future of app management and chances are that you won’t be the only organization looking to unlock this type of feature.
Let’s look at how this actually works. The Managed App Config options per supported app are available on a feed. The feed is available at http://d2e3kgnhdeg083.cloudfront.net. Here, as follows, you’ll see a list of all of the apps supported.
Here, note that most of these fields are key value pairs defined by Adobe (in this example at least). You can enable or disable features of Adobe Reader using these keys. The same is true with a tool like Box that might want a more granular collection of settings than a feature like Managed Open In.
Once you have the XML, you can then copy it to the clipboard and paste it into the App Configuration tab of an app, as follows.