Photo by YEH CHE WEI on Unsplash
The Challenge
If you are building a transit app, static schedules (GTFS) are only half the battle. Users expect live updates: “Is my bus late?” or “Where is it on the map right now?” Most modern transit agencies publish this data using GTFS-Realtime. However, unlike the JSON APIs we are used to in iOS development, these feeds often use Protocol Buffers (.pb files). If you try to read a .pb file like a JSON string, you will get gibberish. This guide walks through the specific tooling and code required to decode these binary files in Swift.
What are Protocol Buffers?
Before writing code, it helps to understand the format.
- Protocol Buffers (Protobuf): A binary format developed by Google. It is significantly smaller and faster to parse than JSON, which is crucial when sending live telemetry for hundreds of vehicles over mobile networks.
- The
.protoFile: Since the data is binary, we need a “blueprint” to understand it. This is a schema file that describes the data structure. - The Code Generator: We use a tool to read the
.protoblueprint and auto-generate a Swift file. This allows us to work with type-safe structs likeFeedMessageorVehiclePositioninstead of raw bytes.
Step 1: Install the Code Generation Tools
To generate the necessary Swift code, you need the Protocol Buffer compiler and the Swift plugin. We will use Homebrew for this.
Open your Terminal and run:
brew install protobuf
brew install swift-protobuf
This installs protoc (the core compiler) and protoc-gen-swift (the Swift plugin).
Step 2: Generate the Swift Schema
For this example, let’s assume we need to fetch real-time transit data for Zootopia. We need the standard GTFS-Realtime schema to decode their data.
- Download the Schema: Get the official
gtfs-realtime.protofile from the Google Developers site. Save it to your project’s root folder. - Run the Compiler: Navigate to your project folder in Terminal and run:
protoc --swift_out=. gtfs-realtime.proto
- Add to Xcode: You will see a new file named
gtfs-realtime.pb.swiftin your folder. Drag this into your Xcode project navigator.
Note: This generated file is large and complex. You don’t need to edit it; simply add it to your project so your app understands the data structure.
Step 3: Add the SwiftProtobuf Library
The generated code relies on Apple’s official Protobuf library to function.
- Open Xcode.
- Go to File > Add Packages…
- Search for:
https://github.com/apple/swift-protobuf - Add the package to your app target.
Step 4: Fetch and Decode the Data
Now we can write the code to fetch live data for example, live vehicle positions from Zootopia. We’ll create a simple TransitFetcher class. Note how we use TransitRealtime_FeedMessage (which comes from the file we generated in Step 2) to decode the binary data.
import Foundation
import SwiftProtobuf
class TransitFetcher {
// Endpoint for Zootopia's transit system
let transitUrl = URL(string: "https://api.zootopia.com/realtime/vehicle_positions.pb")!
func fetchVehicles() {
let task = URLSession.shared.dataTask(with: transitUrl) { data, _, error in
guard let data = data, error == nil else { return }
do {
// 1. Decode the binary data using the generated struct
let feed = try TransitRealtime_FeedMessage(serializedData: data)
// 2. Iterate through the entities
for entity in feed.entity where entity.hasVehicle {
let bus = entity.vehicle
print("Bus #\(bus.vehicle.id) is at: \(bus.position.latitude), \(bus.position.longitude)")
}
} catch {
print("Protobuf decoding error: \(error)")
}
}
task.resume()
}
}
How it works:
- Network Request: We download the raw data just like any other API call.
serializedData: We pass the rawDataobject into theTransitRealtime_FeedMessageinitializer.- Type Safety: The library handles the complex binary parsing, giving us standard Swift properties (
.latitude,.vehicle.id) to work with immediately.
Final Takeaway
Working with GTFS-Realtime might seem intimidating because of the binary format, but the workflow is standardized:
✔️ Install
protocand the Swift plugin
✔️ Generate the.swiftfile from the standard.protoblueprint
✔️ Import theSwiftProtobufpackage
✔️ Decode the data using the generated structs
Once this pipeline is set up, you can easily expand your app to handle trip updates (delays) and service alerts using the exact same logic.
Consider subscribing to my YouTube channel & follow me on X(Twitter). Leave a comment if you have any questions.
Share this article if you found it useful !