Using a Ruby Gem in a Mac App

January 6th, 2019

Filed under: Mac Development | Be the first to comment!

I wanted to run a Ruby tool (packaged as a gem) in a Mac app, but I didn’t want people to have to install the Ruby gem to run my app. I knew I wanted to bundle the gem in my app bundle, but I wasn’t sure how to do so. I finally figured it out so I am sharing what I learned with you. There are five main steps to bundling a Ruby gem in a Mac app and running the gem.

  1. Install the gems on your Mac
  2. Add a Copy Files build phase
  3. Find the gems folder in the app bundle
  4. Configure the command to run the gem
  5. Run the gem in your app

Step 1: Install the Gems on Your Mac

Before you can copy Ruby gems to your app bundle, you must install them on your Mac. The standard gem install command installs the gem in a location that’s difficult to find. What you should do is install the gems you want to use in a folder you can find easily, such as a gems folder inside your project folder.

To install the gems into a custom location, use the -i option and supply the path to the destination folder.

gem install -i /path/to/gem/folder gemName

Step 2: Add a Copy Files Build Phase

You must add a Copy Files build phase to your app target that copies the Ruby gem to the app bundle when building the project. Select your project from the project navigator to open the project editor. Select your target from the project editor. Click the Build Phases button at the top of the project editor.

In Xcode choose Editor > Add Build Phase > Add Copy Files Build Phase.


Use the Destination menu to specify where in the app bundle to copy the files. I chose the Resources folder.

If you want to copy the files into a subfolder, enter the name of the folder in the Subpath text field.

Click the Add button to add the gems to the Copy Files build phase. A sheet will open. Click the Add Other button. Navigate to your gem folder location and select the gem folder containing the gems you want to add.

Step 3: Find the gems Folder in the App Bundle

The next step is to locate your gems folder in the app bundle so you can run the gem and set up environment variables to run the gem. You are going to use the Bundle class. The Bundle class has accessors to get to standard parts of the bundle, such as the Resources folder.

Once you get to a standard location in the bundle, you must build a path to the folder containing your gems. The following code locates a gems folder inside the Resources folder:

if let resourceDirectory = Bundle.main.resourceURL {
    let pathString = resourceDirectory.path + "/gems"
    return URL(fileURLWithPath: pathString)

The folder containing the gems should have a bin folder containing the executable files. You would build a path to the executable file and use that path as the first argument when running the gem.

Step 4: Configure the Command to Run the Gem

Before you can run the gem, you must configure the command to call it. Use the Process class, formerly called NSTask, to run a command-line program, such as a gem.

Start by creating a Process object.

let taskToRun = Process()

You must set up the following items:

  • The launch path
  • Environment variables
  • The arguments to pass

Use the launchPath property to set the launch path. For Ruby gems the launch path is the path to the Ruby interpreter.

taskToRun.launchPath = "/usr/bin/ruby"

Use the environment property to set environment variables. An environment variable is a dictionary. For Ruby gems the environment variable key is GEM_HOME and its value is the path to your gems folder.

taskToRun.environment = ["GEM_HOME" : gemsPath]

Use the arguments property to pass arguments to the gem. The arguments property is an array of strings. The first argument is the path to the executable file of the gem you want to run. Add any arguments the gem requires.

When supplying options as arguments, make sure you have separate entries for the option and its value. Suppose your gem has a -o option followed by the name of an output file. The -o is one entry. The name of the output file is a separate entry.

let argumentList = [gemExecutablePath, inputURL.path, "-o", outputURL.path]
taskToRun.arguments = argumentList

Step 5: Run the Gem

The final step is to run the gem. On macOS 10.13 and later, call the run function to run the task you configured.

do {
} catch {
    fatalError("Error running command line tool.")

On earlier versions of macOS, call the launch function to run the task.


Leave a Reply

Your email address will not be published. Required fields are marked *