How many apps use Swift in 2019?

Three years ago, I read a blog post by Ryan Olsen where he explored how many of the top 100 apps on the app store were using Swift. He was surprised that at the time, only 11% of the top 100 apps were using Swift (I wasn’t).

I thought it would be interesting to revisit this in early 2019. Swift has been out for going on 5 years now, Swift 5 will be released soon, and my perception is that Swift has been broadly adopted across the iOS development community. But, what do the numbers say?

I downloaded the top 110 free apps on the app store on January 15, 2019. I decrypted them, then wrote a script that does some simple analysis of their contents to determine whether or not they’re using Swift, and roughly how much of the app is written in Swift.

Results

According to this analysis, of the top 110 apps on the app store on January 15, 2019, 42% are using Swift, while 58% are not. If only the 79 non-game apps are considered, the results are that 57% are using Swift, while 43% are not.

Pie charts showing apps using Swift

Interestingly, of the 31 games, none are using Swift at all. My guess is that most if not all of them are written using Unity or another cross-platform game engine, and therefore don’t have much if any iOS-specific code. I did look at a few of them myself and noticed that while the games I analyzed do have Objective-C classes, they seem to be mostly code from various analytics and social media frameworks, not code that was actually written specifically for the game itself.

Methodology

The apps were analyzed using a Python script that I wrote. You can find the script in this GitHub repo. A few notes about the way the script works:

In order for an app to be considered to “use Swift”, it must include libswiftCore.dylib in its Frameworks folder, and it must have at least one Objective-C compatible Swift class in the main executable. Some apps don’t use Swift in the main executable but include dynamically linked frameworks that use Swift. For this analysis, those apps are not counted as using Swift, because I wanted to get an idea of how many apps themselves were being developed in Swift.

However, this way of doing analysis is not perfect. For one thing, it will still count an app as using Swift if that app includes a staticly linked Swift library, even if the app’s own code doesn’t use Swift. There’s no foolproof way (that I know of) to automate figuring that out.

For apps using Swift, the script also tries to determine the percentage of each app’s main executable that is written in Swift. It determines the percentage of the app written in Swift by finding all the Objective-C exposed classes, and counting those written in Swift vs. those written in Objective-C. Again, this is imperfect because it doesn’t include Swift types that are not @objc classes. But it does give you a rough ballpark figure for how heavily each app uses Swift. The values here range from a minimum of 1% for the Google Drive app, up to a maximum of 80% for the Walmart app. The average percentage of each Swift-using app written in Swift is 34%.

The determination of whether an app was a game or not was made by hand by me, and is by nature somewhat subjective. For example, I didn’t consider Bitmoji or TikTok games, despite them both being fun entertainment apps.

Takeaway

In the past 3 years, Swift has gone from being used in a small minority of the most popular apps to being used in roughly half of them, which is a huge increase and shows how well Apple has done with introducing a new language. However, even for apps using Swift, they continue to use Objective-C fairly heavily. So, Objective-C is far from dead. Games continue to be written using tools that allow for cross-platform deployment, and are therefore written in languages other than Objective-C and Swift.

Data

You can find the raw data I generated in this GitHub Gist. If you do your own analysis that turns up other interesting insights, I’d love to hear about it!

# How many apps use Swift in 2019?

Three years ago, I read a blog post by Ryan Olsen where he explored how many of the top 100 apps on the app store were using Swift. He was surprised that at the time, only 11% of the top 100 apps were using Swift (I wasn’t).

I thought it would be interesting to revisit this in early 2019. Swift has been out for going on 5 years now, Swift 5 will be released soon, and my perception is that Swift has been broadly adopted across the iOS development community. But, what do the numbers say?

I downloaded the top 100 free apps (give or take) on the app store on January 15, 2019. I decrypted them, then wrote a script that does some simple analysis of their contents to determine whether or not they’re using Swift, and roughly how much of the app is written in Swift.

Results

According to this analysis, of the top 110 apps on the app store on Janurary 15, 2019, 42% are using Swift, while 58% are not. If only non-game apps are considered, the results are 56% using Swift, 44% not.

Methodology

Infrastructure

Intro

I’ve found it difficult the past few years to be productive working on my own app, Aether. Part of the trouble has been that it was always difficult to release a new version. I had to manually build it, write release notes, update and reindex the built in help, create a new DMG, create a ZIP file (for Sparkle), edit the Sparkle appcast XML, and FTP everything to my server. This was a completely manual process, and each step takes a minute or two at the very least. This is just for the non-app-store version, too! It all added up to enough friction that I tended to avoid releasing updates.

Also, often when I’d sit down to work on development, I’d end up spending a significant chunk of time working through a backlog of customer support email. Customer support is very important, but it’s also really time consuming. I’d often find myself answering variations on the same questions over and over. I saved canned responses for a few common issues, but I’d still often spend 10-15 minutes answering a single email.

None of this is the thing I actually love to do: write code! In order to address these problems and give me more time to actually work on developing Aether, I’ve decided to make some infrastructure and process improvements. These have made a HUGE difference, and I’m finding it much more fun to work on the app itself again. I thought I’d outline the solutions that have worked for me, in the hopes that others may find them useful.

Build Server

I started by thinking it would be fun to write a web app to host and manage builds of Aether. Maybe I’d even use Swift on the server! Of course, that would be a huge undertaking, and I was lucky to find that a few people had already written just such a thing. I ended up using Appcaster (with some changes in my own fork), a Node.js app specifically built to host and manage Mac apps that use Sparkle for updates.

Appcaster makes it really easy to upload new builds, automatically generates and serves Sparkle appcasts, lets me write release notes in Markdown, and even allows for multiple deployment channels (e.g. Developer, Beta, Release). It also provides URLs that always point to the download and release notes for the latest version of Aether (DMG and ZIP) on each channel. Links on the Aether website point to these so that uploading a new build no longer requires any manual changes to the website at all.

Uploading New Builds

With Appcaster up and running (I’m using Heroku to host it), I wanted a way to quickly deploy new builds to the server. I wrote a quick-and-dirty Bash script to be run as an Xcode build phase. I put it in a custom target that first builds the app itself, then runs the script. The script takes the built app, creates both DMG and ZIP files for it, uploads them (along with the dSYM) to Amazon S3, then adds a new build to Appcaster. With this script, uploading a new build is as easy as selecting “Upload Aether Build” in Xcode’s scheme selector, then hitting command-B. Builds are deployed only to the Development channel by default, but can easily be promoted to Beta and/or Release. You can find the current (as of this writing) version of the script here. Disclaimer: It’s not polished, and could definitely use some improvement. I’m also far from a Bash expert.

Reducing the Customer Support Burden

A very significant portion of the customer support emails I receive are questions that could be answered by better documentation. I’ve been bad about writing good built in help due to the difficulty involved. Up until now, adding a new help page has meant writing HTML by hand, adding links to it to other pages, updating the help index, then releasing an update to Aether. It is also difficult to point people to specific help pages, even when I do have a page that would help with support.

help.aetherlog.com

The obvious solution is to put help for Aether on the web. I’m not a web developer by any means, so I wanted something that would be easy for me to set up, work well, but most importantly be easy to maintain and improve. I settled on MkDocs, a static site generator specifically designed for doing project documentation. The source for an MkDocs site is just plaintext Markdown files, along with a single YAML configuration file. It’s very easy for me to write with a text editor. Adding a new page is as simple as creating a new Markdown file and adding it to the config.yml file. The site can be rebuilt and deployed to my web host by running a single, simple Bash script. It’s not much more difficult than writing a response email. Now, when I get an email asking a question I know will come up again, I can write a help page and link to it so that my answer is valuable to more than just one person.

I decided to open source the new Aether help site. You can find it on GitHub here.

Built-in Help

Of course, a website for help is great, and really helps me with email customer support. However, I also want users to continue to be able to find and access help from within Aether. Hooking up the Aether Help menu item to open help.aetherlog.com was of course trivial. More complicated is redoing the Help menu search field that is standard in Mac apps so that it searches the website.

It turns out there’s a protocol called NSUserInterfaceItemSearching that allows you to provide custom results when the user searches using the Help menu search field. MkDocs generates a JSON file index of the entire site by default. It was pretty simple todownload and parse that to provide an index for searching. To actually do the searching, I’m using BRFullTextSearch, an Objective-C full text search engine API and wrapper for CLucene. I’m usually very hesitant to include third party libraries, but in this case it solved a problem I wasn’t interested in solving myself.

Conclusion

Good infrastructure can remove many of the tedious tasks associated with releasing, maintaining, and supporting apps, allowing for more time to focus on the fun parts of development. Making these changes has helped me refocus on improving Aether. I hope the tools I’ve talked about here are helpful to others.

Objective-C Class Properties

While looking through the release notes for Foundation in iOS 10/macOS 10.12 Sierra, I noticed an item under “Overall API Updates”

Use of class properties, in both Swift as well as Objective-C, latter using the new “@property (class)” declaration.

Xcode 8 ships with a new versions of Clang and LLVM (800.0.2.24.1, and 8.0.0 as of the first beta). The new version of Clang adds support for class properties in Objective-C. The feature is not yet fully documented anywhere that I can find1, so I did a little experimenting.

Let’s say you have a Car class and want newly created cars to have an optional, configurable default color. In Swift you can already do:

class Car {
    static var defaultColor: UIColor?
}

Previously, in Objective-C, you might have done this using:

@interface Car : NSObject
+ (UIColor *)defaultColor;
+ (UIColor *)setDefaultColor:(UIColor *)defaultColor;
@end

This has long been a fairly common approach, mirroring as it does the convention for instance property accessor methods. It is used in a number of places in the system frameworks on OS X and iOS.

With the new support for Objective-C class properties, you can now do the following:

@interface Car : NSObject
@property (class) UIColor *defaultColor
@end

Here the class specifier in the list of attributes for the @property means this will be a class property rather than an instance property.

It is important to note that unlike regular instance properties, Objective-C class properties can not be synthesized using @synthesize, nor are they automatically synthesized by the compiler. You are responsible for implementing accessor methods for them yourself. (You can also use an @dynamic for them to tell the compiler that accessor methods will be provided at runtime.) If you don’t provide methods for them, you’ll get a compiler warning, and @synthesize produces a compiler error:

Compiler warning/error

The implementation of the methods will depend on exactly what the class property does, but one common approach is to back them with a static variable:

static UIColor *_carDefaultColor = nil;

@implementation Car

+ (UIColor *)defaultColor { return _carDefaultColor; }
+ (void)setDefaultColor:(UIColor *)defaultColor { _carDefaultColor = defaultColor; }

@end

It seems a fair bet that this feature was added for better bridging into Swift, where class properties have always existed. Before real Objective-C class properties, you would have had to call the accessor methods using method/function syntax:

Car.setDefaultColor(.white())

Now, when an Objective-C class with a class property is bridged into Swift, you can use regular property syntax to access the class property:

Car.defaultColor = .white()

Of course, this feature provides a nice benefit even for pure Objective-C code, where you can now use dot-notation to access class properties:

Car.defaultColor = [UIColor whiteColor];

Presumably, in cases where it makes sense, Apple will be using the new Objective-C class properties in system API, improving bridging into Swift. It’s not clear if they will go back and update existing API to use the new syntax. So far, only NSGridView and allowsAutomaticWindowTabbing in NSWindow (both new APIs in 10.12) are using the new syntax, but that may change.

It’s nice to continue to see updates being made to Objective-C. As with last year’s addition of lightweight generics, etc., even if they’re being done primarily for bridging with Swift, they provide some nice improvements for all Objective-C programmers.


  1. It was discussed on the LLVM mailing list and bug tracker during development, of course. ↩︎

Swift on Raspberry Pi

When Apple first announced that Swift was going to be open sourced at WWDC last summer, one of my first hopes was that I could use it to write programs for Raspberry Pi. With Swift’s actual open sourcing earlier this month, I immediately set out to get the Swift compiler up and running on my Raspberry Pi 2. It turned out to be somewhat more difficult than I expected, and I ended up distracted with the holidays, etc. Thankfully, other people were working on it too.

About a week ago, William Dillon (@hpux735) announced that he had gotten the Swift compiler to successfully build for armv7 systems such as the BeagleBone, Raspberry Pi 2, etc. A while later, @iachievedit had packaged William’s work up and made it available via apt-get for Debian and Ubuntu. (Note that Swift is not yet working Raspberry Pi 1. It’s being worked on though.)

Yesterday, using their work I got Swift up and running on my own Raspberry Pi 2, and tweeted about it:

To my surprise, a lot of people were interested, and my tweet was retweeted widely, including by Chris Lattner. A number of people asked for instructions for getting up and running with Swift on Raspberry Pi, so I’m writing those up here.

A few disclaimers: I did very little to make this happen. I mostly just followed @iachievedit’s instructions for installing William Dillon’s Swift ARM packages. Still, there were a few hangups that I had to figure out, and I hope these instructions are useful. Also, Swift on ARM is still in an alpha state. The compiler itself works, but Foundation, and the Swift Package Manager are not working. Until these, especially Foundation, are up and running, it will be somewhat hard to use Swift for anything terribly useful. Anyway, instructions follow:

To start, we need to install Ubuntu 14.04 on an micro SD card (I tried to use Raspbian, and ran into a simple roadblock, but it may be possible too). The instructions for installing Ubuntu are mostly taken from this page and this page. First, download an image of Ubuntu 14.04 for Raspberry Pi 2 here. When the download finishes, unzip the downloaded file somewhere reasonable (I put it on my Desktop).

Connect the SD card to your Mac, then list all connected disks by running the following command in Terminal:

diskutil list

Find the SD card in the list, including its disk number. It will be identified as /dev/disk# where # is the disk number. You’ll need this for later steps.

Next, format the card using FAT32 like so:

diskutil eraseDisk FAT32 PiUbuntu MBRFormat /dev/disk#

(Replace # with the disk number you found above).

Now we’re ready to copy the Ubuntu image to the SD card:

sudo dd bs=1m if=~/Desktop/2015-04-06-ubuntu-trusty/2015-04-06-ubuntu-trusty.img of=/dev/rdisk#

Enter your Mac system password when prompted. (Again, replace # with the disk # found using diskutil list.)

When the copy is done, eject the SD card from your Mac, and insert it into your Raspberry Pi 2, then power up the Pi. Assuming all went well, the Pi should boot to a command prompt. We need to finish some setup steps. First, delete the second partition on the disk, by running:

sudo fdisk /dev/mmcblk0

When prompted, enter ’d’ (for delete), then ‘2’. Then, recreate the partition by entering 'n’, then 'p’, then '2’, then pressing enter at the next two prompts.

Reboot by running

sudo reboot

After the reboot, resize the partition’s file system by running:

sudo resize2fs /dev/mmcblk0p2

Setup a swap file by doing:

sudo apt-get install dphys-swapfile

If you want a GUI desktop, install Lubuntu (or Xubuntu or Kubuntu if you prefer):

sudo apt-get install lubuntu-desktop

Then reboot (sudo reboot).

After all this, we’re ready to install the Swift compiler. Following @iachievedit’s instructions, do the following:

Install libicu-dev and clang-3.6:

sudo apt-get install libicu-dev clang-3.6

Use update-alternatives to provide /usr/bin links for clang and clang++:

sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100

Then, add @iachievedit’s repository key:

wget -qO- http://dev.iachieved.it/iachievedit.gpg.key | sudo apt-key add -

Add the appropriate repository to sources.list:

echo "deb [arch=armhf] http://iachievedit-repos.s3.amazonaws.com/ trusty main" | sudo tee --append /etc/apt/sources.list

Run apt-get update:

sudo apt-get update

Finally, install Swift!

sudo apt-get install swift-2.2

After the installation is complete, you’re ready to compile Swift programs!

Open your favorite text editor, write a program, and save it (e.g. to 'hello.swift’):

let device = "Raspberry Pi 2!"
print("Hello from Swift on \(device)")

Then compile it:

swiftc hello.swift

and run it:

./hello

Hello from Swift on Raspberry Pi 2!

That’s it! Swift running on Raspberry Pi. In order for Swift to be terribly useful on Raspberry Pi 2, more work needs to be done. See the Swift on ARM issue on the Swift bug tracker, and its child cases here. Follow that issue, along with myself, @iachievedit, and @hpux735 to keep up with what’s going on.

One thing that is needed are libraries for using the Pi’s hardware peripherals, including the GPIO pins, i2c, SPI, serial, etc. I’m working now on porting my serial port library, ORSSerialPort to Swift, in preparation for adding Linux support. I’d also like to write a library specifically for Raspberry Pi I/O in Swift, but have not yet started on it. Stay tuned for more.

MIKMIDI - A new Objective-C MIDI library

MIKMIDI is a library intended to simplify implementing MIDI functionality in OS X and iOS apps. We at Mixed In Key have released MIKMIDI as an open source project so that others can use it, and hopefully contribute to improving it. MIKMIDI is available on GitHub here and can also be installed using CocoaPods. In this post I will introduce the library, our motivation for creating it, along with an overview of the library itself.

Origins and Motivation

My day job is doing Mac and iOS development at Mixed In Key. Recently, we released our first live performance DJ software, Flow. I spent most of 2013 working on Flow, and really enjoyed the work we did (and continue to do) on it.

Like most DJ performance software, Flow allows you to connect a DJ controller (like this one, for example) to control the software when you’re on stage. There are many such controllers available from a wide variety of manufacturers, but they all use MIDI to communicate with a Mac or PC.

OS X and iOS include a system framework for dealing with MIDI, called CoreMIDI. CoreMIDI allows you to connect to and communicate with MIDI devices, as well as with other MIDI enabled apps. CoreMIDI is very powerful, but it’s a pure C API, and like its sister framework, CoreAudio, can be a bit daunting and difficult to get started with. Also, as a C API it’s not always a natural fit for a codebase mostly written in Objective-C.

MIKMIDI attempts to alleviate the difficulties of using CoreMIDI directly, by providing a well-written, purely Objective-C API for MIDI. It also adds a number of higher level features useful in a MIDI enabled app beyond the things CoreMIDI already allows.

Overview

There are essentially three major parts of MIKMIDI:

  • Device support – includes support for device discovery, connection/disconnection, and sending/receiving MIDI messages.
  • MIDI commands – includes a number of Objective-C classes that various represent MIDI message types.
  • MIDI mapping – support for generating, saving, loading, and using mapping files that associate physical MIDI controls with corresponding application features.

The first two of these, device support, and MIDI commands, mostly consist of a straightforward Objective-C wrapper for existing CoreMIDI functionality, with functionality enhancements where it makes sense. MIDI Mapping is something that CoreMIDI does not provide at all.

Note that currently, MIKMIDI is focused on working with MIDI communication rather than recorded MIDI files containing music. However, contributions to add features for working with MIDI files are certainly welcome, and I may work on them myself at some point. MIKMIDI’s ultimate goal is to make working with all aspects of MIDI easier in Objective-C.

Device Support

With MIKMIDI device support functionality, you can get a list of available MIDI devices as simply as this:

NSArray *availableDevices = [[MIKMIDIDeviceManager sharedManager] availableDevices];

MIKMIDIDeviceManager’s availableDevices property is Key Value Observing (KVO) compliant, and NSNotifications are also posted anytime a device is connected or disconnected. This makes it quite easy to create a UI that displays a list of connected devices and updates automatically as devices are added and removed.

MIKMIDIDeviceManager also makes it easy to connect to and disconnect from MIDI sources. When connecting to a source, you pass in an event handler block, which is called anytime MIDI messages are received from that source. Multiple event handlers can be connected, allowing multiple objects in an application to receive MIDI messages from the same source.

Sending a MIDI command is as simple as creating an MIKMIDICommand instance and passing it to MIKMIDIDeviceManager’s -sendCommands:toEndpoint:error: method.

MIDI devices are represented by instances of MIKMIDIDevice. MIKMIDIDevice includes properties to get the manufacturer and model name of the device, as well as to access its entities, which contain its endpoints.

MIKMIDI also allows for connecting to virtual MIDI endpoints, in order to communicate with other MIDI apps on the same computer. Available virtual ports can be found by MIKMIDIDeviceManager’s virtualSources and virtualDestinations properties.

MIDI Commands

In MIKMIDI, MIDI messages (aka commands) are Objective-C objects. Specifically, they’re instances of MIKMIDICommand or one of its subclasses. Each of MIKMIDICommand’s subclasses is used to represent a specific type of message. That way, for example, a control change message has methods for accessing controller number and value, while a note on message has methods for note number and velocity. Support for command types beyond those currently directly supported by MIKMIDI can easily be added by subclassing MIKMIDICommand.

The library includes a category on NS/UIApplication to allow easy routing of MIDI messages to interested objects in an application. This system is inspired by the Cocoa event handling architecture. This functionality will be detailed in a later blog post, and more information can be found in the documentation.

MIDI Mapping

MIKMIDI includes features to help with adding MIDI mapping support to an application. MIDI mapping refers to the ability to map physical controls on a particular hardware controller to specific functions in the application. MIKMIDI’s mapping support includes the ability to generate, save, load, and use mapping files that associate physical controls with an application’s specific functionality. A mapping between a specific controller and the application’s UI is represented by an MIKMIDIMapping object. MIKMIDIMappingGenerator can be used to manage mapping files on disk, including both user-generated and application-bundled mappings. Also included is MIKMIDIMappingGenerator, which helps with implementing a system that allows end users to easily generate their own mappings using a “MIDI learn” style interface.

In developing Flow, we found out that MIDI DJ controllers each have their own quirks and differences. The data format for MIKMIDIMapping was informed and driven by our experiences with various controllers, and the different ways they transmit control actions by the user. MIKMIDIMappingGenerator includes algorithms that attempt to interpret the messages coming from a controller and associate them with a control type (e.g. knob, button, jog wheel), without the programmer needing to know about the specific quirks of the controller being used.

What Next?

Complete documentation for MIKMIDI can be found on CocoaDocs. In follow up blog posts, I’ll provide some examples for working with MIKMIDI, including sample code. If you have any questions about MIKMIDI, suggestions for future improvement, or would like to contribute to its development, please email me, or comment below!

ORSSerialPort - A new Objective-C serial port library

ORSSerialPort

I’ve just released ORSSerialPort (github link). ORSSerialPort is my take on a modern, easy-to-use Objective-C serial port library. It’s a simple, Cocoa-like set of Objective-C classes useful for programmers writing Objective-C Cocoa apps that must communicate with external devices through a serial port (most commonly RS-232). Using ORSSerialPort to open a port and send data can be as simple as this:

ORSSerialPort *serialPort = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"];
serialPort.baudRate = [NSNumber numberWithInteger:4800];
[serialPort open];
[serialPort sendData:someData]; // someData is an NSData object
[serialPort close];

ORSSerialPort is released under an MIT license, meaning you’re free to use it in both closed and open source projects. However, even in a closed source project, you must include a publicly-accessible copy of ORSSerialPort’s copyright notice, which you can find in the LICENSE file.

If you have any questions about, suggestions for, or contributions to ORSSerialPort, please contact me. I’d also love to hear about any cool projects you’re using it in.

Background

This post will describe my motivation for writing ORSSerialPort along with how to use it and some implementation details. First, a little background. In early 2008, I released Aether, an amateur radio logging application for Mac OS X. Most commercial amateur radios made in the last 30 years or so have an RS-232 port through which various radio functions and parameters can be controlled and read. Aether uses this capability to automatically insert the frequency and mode the user’s radio is set to without requiring manual entry of these values. This explains my interest in and use for serial ports on Mac OS X.

OS X’s API for talking to serial ports is provided by IOKit along with standard POSIX functions like open(), select() and close(), etc. None of these APIs are high level Objective-C APIs, rather they’re C APIs. In order to easily integrate with other Objective-C Cocoa code, it’s nice to have Objective-C classes that can be used to communicate through serial ports. The currently shipping version of Aether uses the AMSerialPort classes for serial communication. AMSerialPort has served me well, but it’s a little more complex than I’d like, and doesn’t take advantage of the newer multiprocessing approaches provided by Grand Central Dispatch. As part of my work on the next major version of Aether, I decided to write my own serial port library to replace AMSerialPort in Aether. It’s my hope that it will also be useful to others.

How to Use ORSSerialPort

To begin using ORSSerialPort in your project, simply drag the files in the “Source” folder into your Xcode project. ORSSerialPort.h/m are required, while ORSSerialPortManager.h/m are optional, but useful (see below). Next, add #import "ORSSerialPort.h" and ’#import “ORSSerialPortManager.h”’ to the top of the source code files in which you’d like to use ORSSerialPort.

Important Note: ORSSerialPort relies Automatic Reference Counting (ARC). If you’d like to use it in a non-ARC project, you’ll need to open the Build Phases for the target(s) you’re using it in, and add the -fobjc-arc flag to the Compiler Flags column for ORSSerialPort.m and ORSSerialPortManager.m. ORSSerialPort will generate a compiler error if ARC is not enabled.

The ORSSerialPort library consists of only two classes: ORSSerialPort and ORSSerialPortManager. As its name implies, each instance of ORSSerialPort represents a serial port device. There is a 1:1 correspondence between port devices on the system and instances of ORSSerialPort. That means that repeated requests for a port object for a given device will return the same instance of ORSSerialPort.

Opening a Port and Setting It Up

You can get an ORSSerialPort instance either of two ways. The easiest is to use ORSSerialPortManager’s availablePorts array (explained below). The other way is to get a new ORSSerialPort instance using the serial port’s BSD device path:

ORSSerialPort *port = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"];

Note that you must give +serialPortWithPath: the full callout (“cu.*”) path to the device, as shown in the example above.

After you’ve got a port instance, you can open it with the -open method. When you’re done using the port, close it using the -close method.

Port settings such as baud rate, number of stop bits, parity, and flow control settings can be set using the various properties ORSSerialPort provides. Note that all of these properties are Key Value Observing (KVO) compliant. This KVO compliance also applies to read-only properties for reading the state of the CTS, DSR and DCD pins. Among other things, this means it’s easy to be notified when the state of one of these pins changes, without having to continually poll them, as well as making them easy to connect to a UI with Cocoa bindings.

Sending Data

Send data by passing an NSData object to the -sendData: method:

NSData *dataToSend = [self.sendTextField.stringValue dataUsingEncoding:NSUTF8StringEncoding];
[self.serialPort sendData:dataToSend];

Receiving Data

To receive data, you must implement the ORSSerialPortDelegate protocol’s -serialPort:didReceiveData: method, and set the ORSSerialPort instance’s delegate property. As noted below, this method is always called on the main queue. An an example implementation is included below:

- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self.receivedDataTextView.textStorage.mutableString appendString:string];
    [self.receivedDataTextView setNeedsDisplay:YES];
}

ORSSerialPortDelegate

ORSSerialPort includes a delegate property, and a delegate protocol called ORSSerialPortDelegate. The ORSSerialPortDelegate protocol includes two required methods:

- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data;
- (void)serialPortWasRemovedFromSystem:(ORSSerialPort *)serialPort;

Also included are 3 optional methods:

- (void)serialPort:(ORSSerialPort *)serialPort didEncounterError:(NSError *)error;
- (void)serialPortWasOpened:(ORSSerialPort *)serialPort;
- (void)serialPortWasClosed:(ORSSerialPort *)serialPort;

Note: All ORSSerialPortDelegate methods are always called on the main queue. If you need to handle them on a background queue, you must dispatch your handling to a background queue in your implementations of the delegate method.

As its name implies, -serialPort:didReceiveData: is called when data is received from the serial port. Internally, ORSSerialPort receives data on a background queue to avoid burdening the main queue to simply received data. As with all other delegate methods, -serialPort:didReceiveData: is called on the main queue.

-serialPortserialPortWasRemovedFromSystem: is called when a serial port is removed from the system, for example because a USB to serial adapter was unplugged. This method is required because you must release your reference to an ORSSerialPort instance when it is removed. The behavior of ORSSerialPort instances whose underlying serial port has been removed from the system is undefined.

The three optional methods’ function can easily be discerned from their name. Note that -serialPort:didEncounterError: is always used to report errors. None of ORSSerialPort’s methods take an NSError object passed in by reference.

How to Use ORSSerialPortManager

ORSSerialPortManager is a singleton class (one instance per application) that can be used to get a list of available serial ports. It will also handle closing open serial ports when the Mac goes to sleep, and reopening them automatically on wake. This prevents problems I’ve seen with serial port drivers that can hang if the port is left open when putting the machine to sleep. Note that using ORSSerialPortManager is optional. It provides some nice functionality, but only ORSSerialPort is necessary to simply send and received data.

Using ORSSerialPortManager is simple. To get the shared serial port manager:

ORSSerialPortManager *portManager = [ORSSerialPortManager sharedSerialPortManager];

To get a list of available ports:

NSArray *availablePorts = portManager.availablePorts;

ORSSerialPortManager is Key-Value Observing (KVO) compliant for its availablePorts property. This means that you can observe availablePorts to be notified when ports are added to or removed from the system. This also means that you can easily bind UI elements to the serial port manager’s availablePorts property using Cocoa-bindings. This makes it easy to create a popup menu that displays available serial ports and updates automatically, for example.

ORSSerialPortManager’s close-on-sleep, reopen-on-wake functionality is automatic. The only thing necessary to enable it is to make sure that the singleton instance of ORSSerialPortManager has been created by calling +sharedSerialPortManager at least once.

Example Project

Included with ORSSerialPort is a demo application called ORSSerialPortDemo. This is a very simple serial terminal program. It demonstrates how to use ORSSerialPort, and may also be useful for simple testing of serial hardware.

ORSSerialPortDemo includes a dropdown menu containing all available ports on the system, controls to set baud rate, parity, number of stop bits, and flow control settings. Also included are two text fields. One is for typing characters to be sent to the serial port, the other for displaying received characters. Finally, it includes checkboxes corresponding to the RTS, DTR, CTS, DSR, and DCD pins. For the output pins (RTS, DTR), their state can be toggled using their checkbox. The input pins (CTS, DSR, DCD) are read only.

The demo application demonstrates that it is possible to setup and use a serial port with ORSSerialPort without writing a lot of “glue” code. Nearly all of the UI is implemented using Cocoa bindings. With the exception of two lines in ORSAppDelegate.m, the source code for entire application is contained in ORSSerialPortDemoController.h/m.