Skip to content

Repository files navigation

SwiftDOF

Build and Test Documentation Swift 6.2+ Platforms

A parser for FAA Digital Obstacle File (DOF) data.

Overview

SwiftDOF parses FAA Digital Obstacle File (DOF) data, which can be downloaded from https://www.faa.gov/air_traffic/flight_info/aeronav/digital_products/dof/.

The data is parsed into Codable structs that can be used in your Swift project.

Design Philosophy

SwiftDOF follows these design principles:

  • Domain-restricted data: Restrictive enums are favored over open types like Strings wherever possible, ensuring type safety and catching errors at compile time.

  • Foundation integration: The library uses Foundation types wherever possible, such as Measurements instead of raw numeric types for physical values.

  • Strict parsing: The parser throws errors for any malformed or invalid data rather than silently accepting it. This ensures data integrity and makes debugging easier. All parsing errors are reported via either thrown exceptions or the errorCallback parameter.

The DOF format is documented at https://www.faa.gov/air_traffic/flight_info/aeronav/digital_products/dof/media/DOF_README_09-03-2019.pdf.

Requirements

  • Swift 6.2+
  • macOS 26+, iOS 26+, watchOS 26+, tvOS 26+, or visionOS 26+

Installation

Add SwiftDOF to your Package.swift:

dependencies: [
    .package(url: "https://github.com/RISCfuture/SwiftDOF", from: "1.0.0")
]

Then add it to your target:

.target(
    name: "YourTarget",
    dependencies: ["SwiftDOF"]
)

Usage

Loading DOF Data

import SwiftDOF

// Load from a local file
let dof = try DOF(data: Data(contentsOf: fileURL))

// Or load asynchronously from a URL
let dof = try await DOF(url: fileURL)

Querying Obstacles

// Get total count
print("Total obstacles: \(dof.count)")

// Look up by OAS number
if let obstacle = dof.obstacle(for: "01-001307") {
    print("\(obstacle.type) at \(obstacle.city), \(obstacle.state ?? "")")
    print("Height: \(obstacle.heightFtAGL) ft AGL")
}

// Filter by state
let texasObstacles = dof.obstacles(in: "TX")

// Iterate over all obstacles
for obstacle in dof {
    // ...
}

Using Measurement Types

// Heights as Measurement<UnitLength>
let heightAGL = obstacle.heightAGL  // e.g., 500 ft
let heightMeters = heightAGL.converted(to: .meters)

// Coordinates as Measurement<UnitAngle>
let lat = obstacle.latitude
let lon = obstacle.longitude

// Or as CoreLocation coordinate
let coordinate = obstacle.coreLocation

Error Handling

SwiftDOF uses strict parsing and reports all errors. There are two ways to handle parsing errors:

1. Catch thrown errors for fatal issues (invalid format, missing headers):

do {
    let dof = try DOF(data: fileData)
} catch let error as DOFError {
    switch error {
    case .invalidFormat(let formatError):
        print("Invalid format: \(formatError)")
    case .parseError(let field, let value, let line):
        print("Failed to parse \(field) '\(value)' at line \(line)")
    case .lineTooShort(let expected, let actual, let line):
        print("Line \(line) has \(actual) chars, expected \(expected)")
    case .invalidEncoding:
        print("File contains invalid encoding")
    default:
        print("Error: \(error)")
    }
}

2. Use the error callback to handle per-line errors while continuing to parse valid data:

var parseErrors: [(Error, Int)] = []

let dof = try DOF(data: fileData) { error, lineNumber in
    parseErrors.append((error, lineNumber))
    print("Warning: \(error) at line \(lineNumber)")
}

print("Parsed \(dof.count) obstacles with \(parseErrors.count) errors")

The error callback is invoked for each line that fails to parse, allowing you to collect warnings while still loading the rest of the file. This is useful for handling DOF files that may contain occasional malformed records.

Documentation

Online API documentation and tutorials are available at https://riscfuture.github.io/SwiftDOF/documentation/swiftdof/.

DocC documentation is available, including tutorials and API documentation. For Xcode documentation, you can run

swift package generate-documentation --target SwiftDOF

to generate a docarchive at .build/plugins/Swift-DocC/outputs/SwiftDOF.doccarchive. You can open this docarchive file in Xcode for browseable API documentation. Or, within Xcode, open the SwiftDOF package in Xcode and choose Build Documentation from the Product menu.

Testing

SwiftDOF has comprehensive unit tests, which can be run with swift test.

E2E Testing Tool

The SwiftDOF_E2E target is a command-line tool for testing DOF parsing:

# Download and parse current FAA DOF cycle
swift run SwiftDOF_E2E

# Parse a local file
swift run SwiftDOF_E2E -i /path/to/DOF.DAT

# Parse from a URL
swift run SwiftDOF_E2E -i https://aeronav.faa.gov/Obst_Data/DOF_251221.zip

# Output as JSON
swift run SwiftDOF_E2E -f json > obstacles.json

Options:

  • -i, --input <path|url>: Path or URL to DOF file (.dat or .zip). Defaults to current FAA cycle.
  • -f, --format <summary|json>: Output format. Defaults to summary.
  • --report <path>: Write a JSON report of the parse — obstacle count, parse errors and the field each one failed on, and per-region counts.
  • --baseline <path>: Compare counts against an earlier report and record any that moved more than their tolerance.

The tool exits non-zero when any line fails to parse.

Cycle Watch

The DOF Cycle Watch workflow parses each new 56-day cycle as the FAA releases it. A daily probe checks whether the cycle in effect is downloadable yet, and the first time it is, the cycle is parsed end to end and its report kept as an artifact — both the record that the cycle was checked and the baseline the next cycle's counts are compared against. Parse errors, counts that drift, or a file declaring the wrong currency date each open an issue naming the cycle.

To re-check a cycle by hand, run the workflow and give it the currency date from the filename, as YYYY-MM-DD.

About

FAA Digital Obstacle File (DOF) parser for Swift.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages