Library / SDK
armadsen/ORSSerialPort avatar
armadsen/ORSSerialPort

ORSSerialPort: a macOS serial port library for Objective-C and Swift

Serial port library for Objective-C and Swift macOS apps

774 stars189 forksObjective-CMIT

At a glance

What is it?
ORSSerialPort wraps the BSD serial port interface in an Objective-C API that a Swift app can use directly, with a delegate for incoming data, a manager singleton for hot plug and sleep handling, and a packet descriptor for framing. The author wrote it fifteen years ago, still uses it in maintained projects, and says plainly that he is no longer maintaining it for other people.
Who is it for?
ORSSerialPort fits a Mac app that talks to a serial device, whether an Arduino board, a robot, a data acquisition box, or a radio, and that wants a small API instead of a termios wrapper, with the sleep and wake handling taken care of. It does not fit a team that needs an actively maintained dependency, because the author states he is no longer maintaining it and that issue and pull request responses will not come quickly.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Objective-C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Fifteen years old, still in use, no longer maintained

The first section of this README is a note from the author, and it is the most important thing on the page.

He is no longer maintaining this project, at least for other people. He wrote it fifteen years ago and it has served him and many other people well. He continues to use it in actively-developed projects and it continues to work fine. People are welcome to use it, fork it, and so on, but they should not expect responses to issues or pull requests to come quickly, if ever. He is occasionally available for paid consulting work on iOS and Mac apps that connect to custom hardware, whether via serial, Bluetooth, or other means.

That is a candid and unusual statement for a library README, and it changes how you should read the rest of the document. The library is finished rather than abandoned, which is a different thing. The code is stable, the API has not moved, and the documentation is thorough enough that you rarely need the author.

The release history supports that reading. The tagged versions are 2.1.0 from June 2019, 2.0.2 from March 2016, and 2.0.1 from September 2015, so the last release predates the recent activity in the repository by several years, and the last push was on 2026-10-19.

What ORSSerialPort is, is an easy-to-use Objective-C serial port library for macOS, useful for programmers writing Objective-C or Swift Mac apps that communicate with external devices through a serial port, most commonly RS-232. The listed use cases are Arduino projects, robots, data acquisition devices, ham radios, and all kinds of other devices.

The licence is MIT, and the README is precise about what that means in practice: you are free to use it in both closed and open source projects, but even in a closed source project you must include a publicly accessible copy of the copyright notice, which is in the LICENSE file. That is a small obligation that many people assume MIT does not carry, so it is worth reading twice.

Opening a port takes five lines

The README opens the usage section with the smallest possible example, and it is in Swift.

swift
let serialPort = ORSSerialPort(path: "/dev/cu.KeySerial1")
serialPort.baudRate = 4800
serialPort.open()
serialPort.send(someData) // someData is an NSData object
serialPort.close() // Later, when you're done with the port

And the same thing in Objective-C.

objective-c
ORSSerialPort *serialPort = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"];
serialPort.baudRate = @4800;
[serialPort open];
[serialPort sendData:someData]; // someData is an NSData object
[serialPort close]; // Later, when you're done with the port

The two are close enough that the Objective-C version is a mechanical translation, which is the point of building the library in Objective-C with a Swift friendly API. The README says most of the example code is in Swift, but that ORSSerialPort can also easily be used from Objective-C code, and the Examples folder contains Swift and Objective-C versions of all four example projects.

There are two ways to get a port instance, and the choice matters when you are enumerating hardware. The easiest is to use the availablePorts property of ORSSerialPortManager. The other is to construct an ORSSerialPort with the serial port's BSD device path, and the README is emphatic that you must give the initializer the full path to the device.

swift
let port = ORSSerialPort(path: "/dev/cu.KeySerial1")

That path is the /dev/cu.* device, not a /dev/tty.* one, and getting it wrong is the classic first failure when you are talking to a USB serial adapter on a Mac.

Once you have a port you open it, and when you are done you close it. The line settings are ordinary properties on the port object.

swift
port.baudRate = 9600
port.parity = .none
port.numberOfStopBits = 1
port.usesRTSCTSFlowControl = true

Baud rate, parity, number of stop bits, and hardware flow control are all set as properties rather than through a separate configuration call, which keeps the common case short and leaves the unusual case, such as a device that needs RTS and CTS wired, one line away.

Sending, receiving, and the main queue rule

Sending is a single call with a Data object.

swift
let dataToSend = "Hello".data(using: .utf8)
port.send(dataToSend)

Receiving is a delegate method, and the delegate protocol is ORSSerialPortDelegate.

swift
func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) {
    let string = String(data: data, encoding: .utf8)
    print("Got \(string) from the serial port!")
}

One constraint is stated twice in the README and it is the one you must design around. The didReceive method is always called on the main queue.

That is a deliberate choice for a Cocoa library, since the delegate pattern in that world is expected to arrive on the main thread, and it is the right default for a UI application. The cost is that a high rate serial stream delivered on the main queue will block your interface, and a device that streams at kilobit rates will make the app feel as though it has hung. A well written application moves the data off the main queue inside the delegate method, and a badly written one will try to parse a protocol on the main thread and wonder why the frame rate collapsed.

The delegate is not only for data. A port informs its delegate of a range of events, and the README lists them: receipt of data, port open and close events, removal from the system, and errors.

The removal event is worth noting. If a USB serial adapter is unplugged while the app is running, the port goes away, and a delegate that ignores the removal event will keep sending to a port that no longer exists. The library surfaces that as a delegate callback rather than as a silent failure.

The full documentation is in the header, ORSSerialPort.h, and the Getting Started guide in the wiki covers the same ground in more detail.

The manager handles hot plug, sleep, and wake

ORSSerialPortManager is a singleton class, one instance per application, and it exists to deal with the parts of a Mac serial port that are not about sending bytes.

swift
let ports = ORSSerialPortManager.shared().availablePorts

The available ports property can be observed with Key Value Observing, so you are notified when a USB to serial adapter is plugged in or removed. It also posts NSNotifications when those events occur, which is the older mechanism and the one most Cocoa code still listens for.

The behaviour that matters most is the sleep handling. The manager will close open serial ports when the Mac goes to sleep and reopen them automatically on wake. The README explains why in the author's own terms: this prevents problems he has seen with serial port drivers that can hang if the port is left open when putting the machine to sleep.

That is a very specific failure mode, and it is the kind of thing that only shows up after a year of using a laptop, so it is worth knowing that the library handles it rather than discovering it yourself.

The manager is optional, and the README says so plainly: it provides some nice functionality, but only ORSSerialPort is necessary to simply send and receive data. That is a sensible layering. The port object is the thing your code actually uses, and the manager is the piece that keeps the world around it consistent, and an application that does not need enumeration, hot plug notifications, or sleep handling can ignore it entirely.

The documentation for the manager is in ORSSerialPortManager.h and in the Getting Started guide of the wiki.

Framing is your problem, and there is a helper for it

The README is honest about the boundary of a low level serial library.

Incoming serial data is delivered to your application as it is received, and a low level library like ORSSerialPort has no way of knowing anything about the structure and format of the data you are sending and receiving.

That is the right design, because a device that speaks a binary protocol, a device that speaks NMEA, and a device that speaks line based text have nothing in common, and a library that guessed would be wrong for all three. So ORSSerialPort hands you bytes and leaves the interpretation to you.

The class named ORSSerialPacketDescriptor is the part that helps. It exists to describe the shape of a packet so the library can extract them from the incoming byte stream for you, and the example projects in the repository are named after the two things you have to write yourself. There is a PacketParsingDemo and a RequestResponseDemo, alongside the ORSSerialPortDemo and a CommandLineDemo, and the README notes that the Examples folder contains both a Swift and an Objective-C version of all four.

The RequestResponseDemo is the one to look at first for most hardware, because request and response over a serial line is the common case: you send a query, wait for a reply, and match them up. The PacketParsingDemo is the one to look at if the device streams continuously, because then the problem is not matching requests to replies but finding the frame boundaries in a stream that can split a frame across two reads or deliver two frames in one.

Both of those problems are solved in your code, and the library gives you the tools to solve them: a delegate that reports every chunk that arrives, and a descriptor that knows where the boundaries are.

Four ways to add it, and where the documentation lives

There are four routes to get the library into a project, and the README points at a wiki guide for the details of each.

You can use the included framework project, which is a checked-in Xcode framework target in the Framework Project directory, so you can drag it into your workspace and build it yourself. You can use Carthage. You can use CocoaPods, and the repository carries a podspec, ORSSerialPort.podspec, for that. And you can use the Swift Package Manager, with a Package.swift at the root, which is the route that needs the least ceremony if your project is already set up for it.

The repository layout supports all of that, with the library sources in Sources, the tests in Tests, the four example projects in Examples, and a.cocoadocs.yml file that drives the generated API documentation.

The documentation itself is in two places. The complete technical documentation is published as a CocoaDocs docset at cocoadocs.org, and the wiki holds the guides, among them the guide to installing the library and the Getting Started guide with its sections on opening a port and setting it up, on the delegate protocol, and on the manager.

On the practical side, the project carries a CHANGELOG.md and a CONTRIBUTING.md, and it is open source under MIT, so forking is a normal move rather than an exception. Given the author's note at the top of the README, forking is arguably the expected path for anyone who needs changes, and the only cost of that is losing whatever the author might have added later, which on the evidence of the release history is very little.

The one thing a fork has to carry with it is the licence obligation. MIT, with the requirement to include a publicly accessible copy of the copyright notice, in closed source projects as well as open ones.

Editorial conclusion

ORSSerialPort fits a Mac app that talks to a serial device, whether an Arduino board, a robot, a data acquisition box, or a radio, and that wants a small API instead of a termios wrapper, with the sleep and wake handling taken care of. It does not fit a team that needs an actively maintained dependency, because the author states he is no longer maintaining it and that issue and pull request responses will not come quickly. Verify first that the device you want to talk to appears in ORSSerialPortManager's available ports and that you use the full BSD device path, that your app's deployment target is compatible with the framework, and that you have read the MIT licence obligation to include the copyright notice even in a closed source product.

Frequently asked questions

Is ORSSerialPort still maintained?

Not for other people, by the author's own statement at the top of the README. He wrote it fifteen years ago, still uses it in actively developed projects, and says not to expect quick responses to issues or pull requests, though he is occasionally available for paid consulting on Mac and iOS apps that talk to custom hardware.

How do I open a serial port with ORSSerialPort in Swift?

Create an ORSSerialPort with the full BSD device path, for example ORSSerialPort(path: "/dev/cu.KeySerial1"), set the baud rate and the other line settings as properties, then call open() and close() when you are done. To enumerate devices instead, read the availablePorts property of ORSSerialPortManager.

How do I receive data with ORSSerialPort?

Implement the ORSSerialPortDelegate protocol and set the delegate property on the port. The serialPort(_:didReceive:) method is always called on the main queue, so anything expensive should be moved off it. The delegate also reports port open and close events, removal from the system, and errors.

What does ORSSerialPortManager add to the library?

It is a singleton that lists the available serial ports, notifies you through Key Value Observing or NSNotifications when a USB to serial adapter is plugged in or removed, and closes open ports when the Mac goes to sleep and reopens them on wake, which prevents the hangs the author saw with drivers that leave the port open.

How can I add ORSSerialPort to my project?

There are four routes, the included framework project, Carthage, CocoaPods with the ORSSerialPort.podspec in the repository, and the Swift Package Manager with the Package.swift at the root. The wiki has a guide to installing the library with the details for each method.

Official sources

  1. armadsen/ORSSerialPort on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/armadsen-orsserialport.svg)](https://hysenlabs.com/projects/armadsen-orsserialport)

Was this useful?