Contents

weppos/uddf-swift

A comprehensive Swift library for parsing and writing UDDF (Universal Dive Data Format) v3.2.1 files.

Overview

UDDF is an XML-based format for dive data exchange between applications and dive computers. This library provides type-safe parsing and writing capabilities with a clean, modern Swift API.

Features

  • Type-safe parsing: Convert UDDF XML into Swift structs with full type safety
  • Writing support: Generate valid UDDF files from Swift models
  • Error handling: Comprehensive error types with clear messages
  • Round-trip fidelity: Parse → write → parse produces identical results
  • Real-world extensions: Supports common non-standard fields
  • iOS and macOS support: Works on iOS 15+ and macOS 12+

UDDF Extensions

UDDF Swift supports a small number of non-standard tags that appear in real-world exports. These are treated as optional extensions and clearly labeled as such.

profiledata:

  • informationbeforedive -> salinity: A libdivecomputer de-facto standard used by some dive computers.
  • samples -> waypoint -> heartrate: Heart rate in beats per minute. Used by dive computers with heart rate monitoring (e.g., Garmin Descent series).

gasdefinitions:

  • mix -> usage: A libdivecomputer extension for gas mix usage type (oxygen, diluent, sidemount).

Installation

Swift Package Manager

Add this package to your Package.swift:

dependencies:
[
    .package(url: "https://github.com/weppos/uddf-swift.git", from: "1.0.0")
]

Or add it in Xcode:

  1. File → Add Package Dependencies
  2. Enter the repository URL
  3. Select the version

Usage

Parsing UDDF Files

import UDDF

// Parse from Data
let xmlData = try Data(contentsOf: uddfFileURL)
let document = try UDDFSerialization.parse(xmlData)

// Parse directly from file
let document = try UDDFSerialization.parse(contentsOf: uddfFileURL)

// Access parsed data
print("Generator: \(document.generator.name)")
print("Version: \(document.version)")

Writing UDDF Files

import UDDF

// Create a document
let generator = Generator(
    name: "MyDiveApp",
    version: "1.0.0",
    manufacturer: Manufacturer(
        id: "mycompany",
        name: "My Company",
        contact: Contact(
            email: "info@example.com",
            homepage: "https://example.com"
        )
    )
)

let document = UDDFDocument(
    version: "3.2.1",
    generator: generator
)

// Write to Data
let xmlData = try UDDFSerialization.write(document)

// Write directly to file
try UDDFSerialization.write(document, to: outputURL)

// Write without pretty printing (compact XML)
let compactData = try UDDFSerialization.write(document, prettyPrinted: false)

Error Handling

do {
    let document = try UDDFSerialization.parse(xmlData)
    // Process document
} catch UDDFError.invalidXML(let detail) {
    print("Invalid XML: \(detail)")
} catch UDDFError.missingGenerator {
    print("UDDF files must contain a generator section")
} catch UDDFError.fileNotFound(let url) {
    print("File not found: \(url.path)")
} catch {
    print("Unexpected error: \(error)")
}

Validation

Validate UDDF documents with comprehensive checks:

import UDDF

let document = try UDDFSerialization.parse(xmlData)

// Basic validation
let result = UDDFSerialization.validate(document)

if result.isValid {
    print("✓ Document is valid")
} else {
    print("✗ Found \(result.errors.count) error(s):")
    for error in result.errors {
        print("  - \(error)")
    }
}

if result.hasWarnings {
    print("⚠ \(result.warnings.count) warning(s):")
    for warning in result.warnings {
        print("  - \(warning)")
    }
}

// Parse and validate in one step
let (doc, validation) = try UDDFSerialization.parseAndValidate(xmlData)

// Strict mode (warnings become errors)
var options = UDDFValidator.Options()
options.strictMode = true
let strictResult = UDDFSerialization.validate(document, options: options)

// Disable range validation for special cases
options.validateRanges = false
let lenientResult = UDDFSerialization.validate(document, options: options)

Reference Resolution

Validate ID/IDREF cross-references:

import UDDF

// Parse and resolve all references
let (document, resolution) = try UDDFSerialization.parseAndResolve(xmlData)

if resolution.isValid {
    print("✓ All references resolved successfully")
    print("  Registry contains \(resolution.registry.count) elements")
} else {
    print("✗ Reference errors:")
    for error in resolution.errors {
        print("  - \(error.message) at \(error.location)")
    }
}

// Or resolve separately
let document = try UDDFSerialization.parse(xmlData)
let resolution = try UDDFSerialization.resolveReferences(in: document)

Working with Unit Types

UDDF Swift provides type-safe units with automatic conversion:

import UDDF

// Depth
let depth = Depth(meters: 18.0)
print(depth.meters)  // 18.0
print(depth.feet)    // 59.06 (automatic conversion)

let depthFromFeet = Depth(feet: 60.0)
print(depthFromFeet.meters)  // 18.288

// Temperature
let temp = Temperature(kelvin: 300.0)
print(temp.kelvin)      // 300.0
print(temp.celsius)     // 26.85
print(temp.fahrenheit)  // 80.33

// Pressure
let pressure = Pressure(bar: 200.0)
print(pressure.bar)    // 200.0
print(pressure.psi)    // 2900.75

// Duration
let duration = Duration(seconds: 1500.0)
print(duration.seconds)  // 1500.0
print(duration.minutes)  // 25.0
print(duration.hours)    // 0.417

Requirements

  • iOS 15.0+ / macOS 12.0+
  • Swift 5.9+
  • Xcode 15.0+

Dependencies

UDDF Specification

This library implements the UDDF v3.2.1 specification:

https://www.streit.cc/extern/uddf_v321/en/index.html

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests. See CONTRIBUTING.md for guidelines.

Changelog

See CHANGELOG.md for release history.

License

MIT License

Package Metadata

Repository: weppos/uddf-swift

Default branch: main

README: README.md