NSTask Tutorial for OS X

In this OS X NSTask tutorial, learn how to execute another program on your machine as a subprocess and monitor its execution state while your main program continues to run. By Warren Burton.

Leave a rating/review
Save for later
Share
Update note: This NSTask Tutorial for OS X has been updated to Swift by Warren Burton. The original tutorial was written by Andy Pereira.

Your Mac has a fully-fledged version of UNIX as its operating system, which means it has a massive amount of pre-installed command line utilities and scripting languages. Swift, Perl, Python, Bash, Ruby, plus anything you can install. Wouldn’t it be nice to use to that power to create awesome apps?

NSTask allows you to execute another program on your machine as a subprocess and monitor its execution state while your main program continues to run. For example, you could run the ls command to display a directory listing — from right inside your app!

A good analogy for NSTask is a parent-child relationship. A parent can create a child and tell it to do certain things, and (theoretically) the child must obey. NSTask behaves in a similar fashion; you start a “child” program, give it instructions and tell it where to report any output or errors. Better yet — it’s way more obedient than your average toddler :]

A great use for NSTask is to provide a front-end GUI to command line programs. Command line programs are powerful, but they require you to remember exactly where they live on your machine, how to call them and what options or arguments you can provide to the program. Adding a GUI on the front end can provide the user with a great deal of control — without having to be a command line guru!

This tutorial includes an NSTask example that shows you how to execute a simple command program with arguments and display its standard output as it runs in a text field. By the end, you’ll be ready to use NSTasks in your own apps!

Note: This tutorial assumes you have some basic familiarity with Mac OS X development and the Terminal. If you are completely new to programming for the Mac, check out our beginning Mac OS X development tutorial series.

Getting Started

To keep the focus squarely on NSTask, I’ve created a starter project for you that contains a basic user interface. Download the project, open it in Xcode, and build and run.

The starter app has one window, as shown:

initial_view

This window has the title “TasksProject”. It has a simple GUI that will, by invoking a shell script, let you build an iOS project, create an ipa and observe what is happening.

Creating Your First NSTask

The NSTask example will be to build and package an iOS app into an ipa file by using NSTask to run some command line tools in the background. Most of the basic UI functionality is in place — your job is do the heavy lifting with NSTask.

Note: It’s recommended that you have an iOS Developer account with Apple, as you’ll need the proper certificates and provisioning profile to create an ipa file that can be installed on one of your devices. Don’t worry if you don’t, though, as you’ll be able to follow the entire tutorial even without an account.

You are now going to work on the embedded View Controller titled “Tasks View Controller”. The first section in the window asks the user to select an Xcode project directory. To save time, rather than having to select a directory manually every time you run this app while testing, you’ll hard-code it to one of your own Xcode project directories.

To do this, head back to Xcode and open TasksViewController.swift. Take a look at the properties and methods under the comment “Window Outlets”:

//View Controller Outlets
@IBOutlet var outputText:NSTextView!
@IBOutlet var spinner:NSProgressIndicator!
@IBOutlet var projectPath:NSPathControl!
@IBOutlet var repoPath:NSPathControl!
@IBOutlet var buildButton:NSButton!
@IBOutlet var targetName:NSTextField!

All of these properties correspond to the Tasks View Controller Scene in Main.storyboard. Notice the projectPath property — this is the one you want to change.

Open Main.storyboard and click on the Project Location item. You’ll find it 4 levels deep in the object hierarchy; it’s a child of the Stack View. In the Attributes Inspector, under Path Control, find the Path element:

set_path_1b

Set Path to a directory on your machine that contains an iOS project. Make sure you use the parent directory of a project, not the .xcodeproj file itself.

Note: If you don’t have any iOS projects on your machine, download a simple project here and unzip it to a location on your machine. Then set the Path property in your application using the instructions above. For example, if you unzip the package on your desktop, you would set Path to
/Users/YOUR_USERNAME_HERE/Desktop/SuperDuperApp
.

Now that you have a default source project path in your app to facilitate testing, you will also need a default destination path. Open Main.storyboard and click on the Build Repository item.

In the Attributes Inspector, find Path item under Path Control:

set_path_2b

Set the Path entry to a directory on your machine that’s easy to find, like the Desktop. This is where the .ipa file created by this application will be placed.

There are two additional fields in the Tasks View Controller Scene you need to know about: the Target Name and an associated text field.

initial_view_2

  1. Target Name is designated for the name of the iOS Target you want to build.
  2. The text area below Target Name will display output from the NSTask object in your project as it runs.

Don’t know the target name of your iOS project? To find it, select your project in Xcode’s project navigator and look under TARGETS in the Info tab. The screenshot below shows where to find this for a sample project called “SuperDuperApp”:

target_name

Remember the target name — you’ll be entering it into the running app later on.

Let’s start fleshing out the bits of code that will run when the “Build” button is pressed.

Preparing the Spinner

Open TasksViewController.swift and add the following code to startTask(_:):

//1.
outputText.string = ""

if let projectURL = projectPath.url, let repositoryURL = repoPath.url {
  
  //2.
  let projectLocation = projectURL.path
  let finalLocation = repositoryURL.path
  
  //3.
  let projectName = projectURL.lastPathComponent
  let xcodeProjectFile = projectLocation + "/\(projectName).xcodeproj"
  
  //4.
  let buildLocation = projectLocation + "/build"
  
  //5.
  var arguments:[String] = []
  arguments.append(xcodeProjectFile)
  arguments.append(targetName.stringValue)
  arguments.append(buildLocation)
  arguments.append(projectName)
  arguments.append(finalLocation)
  
  //6.
  buildButton.isEnabled = false
  spinner.startAnimation(self)
    
}

Here’s a step-by-step explanation of the code above:

  1. outputText is the large text box in the window; it will contain all the output from the script that you will be running. If you run the script multiple times, you’ll want to clear it out between each run, so this first line sets the string property (contents of the text box) to an empty string.
  2. The projectURL and repositoryURL objects are NSURL objects, and this gets the string representations of these objects in order to pass them as arguments to your NSTask.
  3. By convention, the name of the folder and the name of the project file are the same. Getting the lastPathComponent property of the project folder contained in projectURL and adding an “.xcodeproj” extension gets the path to the project file.
  4. Defines the subdirectory where your task will store intermediate build files while it’s creating the ipa file as build.
  5. Stores the arguments in an array. This array will be passed to NSTask to be used when launching the command line tools to build your .ipa file.
  6. Disables the “Build” button and starts a spinner animation.

Why disable the “Build” button? The NSTask will run each time the button is pressed, and as the app will be busy for an amount of time while the NSTask does its work, the user could impatiently press it many times — each time spawning a new build process. This action prevents the user from creating button click events while the app is busy.

Build and run your application, then hit the Build button. You should see the “Build” button disable and the spinner animation start:

busy1

Your app looks pretty busy, but you know right now it’s not really doing anything. Time to add some NSTask magic.

Contributors

Over 300 content creators. Join our team.