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.

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

Approve Or Deny GSuite Access For Devices

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
import googleapiclient.discovery

SCOPES = ['https://www.googleapis.com/auth/admin.directory.device.mobile']
SERVICE_ACCOUNT_FILE = 'auth.json'
EMAIL_ACCOUNT = ''


def get_credential():
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject(EMAIL_ACCOUNT)
# admin = googleapiclient.discovery.build('admin', 'directory_v1', credentials=credentials)
admin = googleapiclient.discovery.build('admin', 'directory_v1', credentials=delegated_credentials)
return admin


def get_mobiledevice_list(admin, customerId):
results = admin.mobiledevices().list(customerId=customerId).execute()
mobiledevices = results.get('mobiledevices', [])
print('mobile devices name and resourceId')
for mobiledevice in mobiledevices:
print(u'{0} ({1})'.format(mobiledevice['name'], mobiledevice['resourceId']))
return results


def action_mobiledevice(admin, customerId, resourceId, actionName): # actionName: "approve", "block",etc body = dict(action=actionName)
results = admin.mobiledevices().action(customerId=customerId, resourceId=resourceId, body=body).execute()
return results


def main():
admin = get_credential()
customerId = ''
resourceId = ''
action = "approve"
#action = "block"

mobiledevice_list = get_mobiledevice_list(admin, customerId)
print(mobiledevice_list)

action_mobiledevice(admin, customerId, resourceId, action)
print ("Approved successfully")


if __name__ == '__main__':
main()

This is likely to evolve, given that you’ll likely want to migrate your settings into a database as part of your build process, but the general logic is here for now. Happy Googleatinging!

Manage the Look of Launchpad

You can control the number of columns and rows in LaunchPad. To do so, edit the com.apple.doc defaults domain with the key springboard-rows for the number of rows to display and springboard-columns to control the number of columns displayed. So to set the number of rows LaunchPad will show per screen, send the write verb into defaults for com.apple.dock along with the springboard-rows and an -int of 4:

defaults write com.apple.dock springboard-rows -int 4

Likewise, to set columns to 8:

defaults write com.apple.dock springboard-columns -int 8

Then just killall for Dock:

killall Dock

In some cases you will also need to send a resetlaunchpad boolean into com.apple.dock (for TRUE) along with a killall for Dock (or reboot):

defaults write com.apple.dock resetlaunchpad -bool TRUE; killall Dock

Setup Google Cloud Functions

Google Cloud Functions provide a streamlined method for running a simple micro-service leveraging custom functions as well as SDKs for any Google service that can be imported into your script. Currently, node.js is the only non-beta language you can build scripts in.

Permissions

Before you setup Google Cloud Functions in your G Suite domain, first provide the account of a developer with the appropriate permissions, identified in the attached screen. 

Enable The SDKs You Need

G Suite has a number of features exposed to their API by importing SDKs into projects. As an example, the Admin SDK provides us with endpoints and classes that make developing micro services to perform actions in the G Suite admin portal easier. In this section we’ll import that SDK, although the tasks for importing other SDKs is similar. 

To get started, open the Google Cloud Platform using the button in the upper left hand corner and click on APIs and Services (the names of these buttons change over time).

TheClick on the Enable APIs and Services button in the dashboard.

Under Credentials, provide the appropriate credentials for the app you’re importing the SDK into.

Search for Admin SDK in the search dialog.

Click Admin SDK, made by Google.

Click Enable.

Once enabled, you’ll need to create a service account for your function to communicate with.

Setup A Service Account

Service accounts give you a JWT, useful to authenticate from a Google Cloud Function back to an instance of the GSuite Admin portal endpoints. To setup a Service account, go to “IAM & admin” using the button in the upper left hand corner. 

Click on Services Accounts.

Provide a project name and a location (if your organization uses locations, otherwise leave that set to No Organization.

Create Your Google Cloud Function

The Google Cloud Function is the microservice that you can then call. This might be sending some json from an app to perform a task from an app, or sending a webhook to the function to perform an action. To get started with functions, click Cloud Function at the bottom of the Google Cloud Platform dashboard.

If functions aren’t enabled, click Enable Billing.

If necessary, click UPGRADE.

The function api will also need to be enabled; if so, click Enable API.

Once all of this is done, you should have a button that says Create function. Click that and then you’ll be able to provide settings for the function.

Settings include the following:

  • Name: How the function is called in the admin panel. 
  • Memory allocated: How much memory the function can consume.
  • Trigger: Most will use HTTP for our purposes.
  • URL: The URL you use to call the function. 
  • Source: The code (typically node.js) that is run.

Note: The package.json allows for us to leverage this function in a multi-tenant fashion. 

Once enabled, you can hit the endpoint. If there’s no header parameters you need to send, that could be as simple as:

curl https://us-central1-alpine-canto-231018.cloudfunctions.net/test-function

Frameworks

A framework is a type of bundle that packages dynamic shared libraries with the resources that the library requires, including files (nibs and images), localized strings, header files, and maybe documentation. The .framework is an Apple structure that contains all of the files that make up a framework.

Frameworks are stored in the following location (where the * is the name of an app or framework):

  • /Applications/*contents/Frameworks
  • /Library/*/
  • /Library/Application Support/*/*.app/Contents/
  • /Library/Developer/CommandLineTools/
  • /Library/Developer/
  • /Library/Frameworks
  • /Library/Printers/
  • /System/iOSSupport/System/Library/PrivateFrameworks
  • /System/iOSSupport/System/Library/Frameworks
  • /System/Library/CoreServices
  • /System/Library/Frameworks
  • /System/Library/PrivateFrameworks
  • /usr/local/Frameworks 

If you just browse through these directories, you’ll see so many things you can use in apps. You can easily add an import followed by the name in your view controllers in Swift. For example, in /System/Library/Frameworks you’ll find the Foundation.framework. Foundation is pretty common as it contains a number of APIs such as NSObject (NSDate, NSString, and NSDateFormatter). 

You can import this into a script using the following line:

import Foundation


As with importing frameworks/modules/whatever (according to the language) – you can then consume the methods/variables/etc in your code (e.g.  let url = NSURL(fileURLWithPath: “names.plist”).