A Swift library for parsing GRIB (GRIdded Binary) meteorological data files.
- Parse GRIB Edition 1 files
- Extract decoded values with proper coordinate mapping
- Wind data utilities for processing U/V components
- Clean, type-safe API with full documentation
- Sendable-compliant types for Swift concurrency
- Cross-platform support (macOS, iOS, tvOS, watchOS, visionOS)
- Swift 5.9+
- macOS 13.0+ / iOS 16.0+ / tvOS 16.0+ / watchOS 9.0+ / visionOS 1.0+
Add SwiftGrib to your Package.swift:
dependencies: [
.package(url: "https://github.com/ghotihook/SwiftGRIB.git", from: "1.0.0")
]Then add it to your target dependencies:
.target(
name: "YourApp",
dependencies: ["SwiftGrib"]
)- File > Add Package Dependencies
- Enter the package URL
- Add to your target
import SwiftGrib
let parser = GribParser()
// Parse from file
let messages = try parser.parse(contentsOf: url)
// Or from data
let messages = try parser.parse(data: gribData)
// Iterate through messages
for message in messages {
print("Parameter: \(message.parameter.name)")
print("Level: \(message.level)")
print("Time: \(message.timestamp)")
print("Grid: \(message.grid?.ni ?? 0) x \(message.grid?.nj ?? 0)")
print("Values: \(message.values.count)")
}// Get value by linear index
if let value = message.value(at: 0) {
print("First value: \(value)")
}
// Get value by grid position
if let value = message.value(i: 10, j: 5) {
print("Value at (10, 5): \(value)")
}
// Get coordinate for an index
if let coord = message.coordinate(at: 100) {
print("Lat: \(coord.latitude), Lon: \(coord.longitude)")
}
// Get min/max values
if let min = message.minValue, let max = message.maxValue {
print("Range: \(min) to \(max)")
}import SwiftGrib
let parser = GribParser()
let messages = try parser.parse(contentsOf: url)
let windProcessor = WindProcessor()
// Extract wind data points (combines U and V components)
let windData = windProcessor.extractWindData(from: messages, sampleStep: 3)
for point in windData {
print("Location: (\(point.latitude), \(point.longitude))")
print("Speed: \(point.speed) m/s (\(point.speedKnots) knots)")
print("Direction: \(point.direction) degrees")
}
// Get available timestamps
let timestamps = windProcessor.availableTimestamps(in: messages)
// Filter by parameter
let uWindMessages = windProcessor.filterMessages(messages, byParameter: GribParameter.uWind)if let grid = message.grid {
print("Grid size: \(grid.ni) x \(grid.nj) = \(grid.totalPoints) points")
print("Bounds: \(grid.bounds.minLatitude) to \(grid.bounds.maxLatitude)")
print("Resolution: \(grid.latitudeIncrement) x \(grid.longitudeIncrement)")
print("Scans W-E: \(grid.scansWestToEast)")
print("Scans N-S: \(grid.scansNorthToSouth)")
// Check if a point is within bounds
if grid.bounds.contains(latitude: -37.0, longitude: 145.0) {
print("Point is within grid bounds")
}
}The main parser class. Thread-safe and Sendable-compliant.
A single GRIB message containing:
messageNumber- Message index within the file (1-based)totalLength- Total length of this message in bytesparameter- What's being measured (wind, temperature, etc.)level- Vertical level (surface, altitude, pressure level)timestamp- Reference timegrid- Grid definitionvalues- Decoded data valuesminValue/maxValue- Data range
Meteorological parameter information:
id- WMO parameter codename- Human-readable nameunit- Unit of measurement
Common parameter IDs:
GribParameter.uWind(33) - U-component of windGribParameter.vWind(34) - V-component of windGribParameter.temperature(11) - TemperatureGribParameter.pressure(1) - PressureGribParameter.relativeHumidity(52) - Relative humidity
Vertical level information:
type- Level type codetypeName- Human-readable type namevalue- Level value
Common level types:
GribLevel.surface(1) - SurfaceGribLevel.isobaric(100) - Isobaric (pressure) levelGribLevel.heightAboveGround(105) - Height above ground
Grid definition:
ni,nj- Grid dimensionsbounds- Geographic boundslatitudeIncrement,longitudeIncrement- Grid spacingscanningMode- How the grid is traversed
Geographic bounds with helper methods:
minLatitude,maxLatitude,minLongitude,maxLongitudecenterLatitude,centerLongitudelatitudeSpan,longitudeSpancontains(latitude:longitude:)
Combined wind data at a point:
latitude,longitude- Locationspeed- Wind speed (m/s)direction- Meteorological direction (degrees, 0 = North)timestamp- Timestamp of the dataaltitude- Optional altitude/level valuespeedKnots,speedKmh,speedMph- Unit conversionsuComponent,vComponent- Calculated components
Utilities for wind data processing:
extractWindData(from:sampleStep:)- Combine U/V into speed/directioncalculateWindSpeedAndDirection(u:v:)- Single point calculationavailableTimestamps(in:)- Get unique timestampsfilterMessages(_:byParameter:)- Filter by parameter
do {
let messages = try parser.parse(contentsOf: url)
} catch GribError.invalidMagic {
print("Not a valid GRIB file")
} catch GribError.unsupportedEdition(let edition) {
print("GRIB edition \(edition) not supported")
} catch GribError.truncatedData(let section) {
print("File truncated in \(section)")
} catch {
print("Error: \(error)")
}- Indicator Section (IS)
- Product Definition Section (PDS)
- Grid Definition Section (GDS) - Lat/Lon grids
- Binary Data Section (BDS)
- Sign-magnitude encoding for coordinates and scale factors
- Bit-packed data extraction
- GRIB Edition 2
- Complex packing schemes
- Bitmap sections (BMS)
- Non-rectangular grids (polar stereographic, Lambert conformal, etc.)
All types in SwiftGrib are Sendable-compliant and safe to use with Swift concurrency:
await withTaskGroup(of: [GribMessage].self) { group in
for url in gribFiles {
group.addTask {
let parser = GribParser()
return try! parser.parse(contentsOf: url)
}
}
// ...
}Contributions are welcome! Please feel free to submit a Pull Request.
SwiftGrib is available under the Apache License, Version 2.0. See the LICENSE file for more info.