Contents

truewebber/swift-protoreflect

A Swift library for dynamic Protocol Buffers message manipulation without pre-compiled schemas.

Overview

SwiftProtoReflect enables runtime manipulation of Protocol Buffers messages without requiring code generation from .proto files. This is useful for building generic tools, API gateways, data processors, and other applications that need to work with protobuf schemas dynamically.

Installation

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/truewebber/swift-protoreflect.git", from: "6.0.0")
]

Note: If you are upgrading from 4.x, see the Migration Guide for details on breaking changes.

Basic Usage

Creating Messages Dynamically

import SwiftProtoReflect

// Define a message schema at runtime
var personSchema = MessageDescriptor(name: "Person", fullName: "Person")
personSchema.addField(FieldDescriptor(name: "name", number: 1, type: .string))
personSchema.addField(FieldDescriptor(name: "age", number: 2, type: .int32))
personSchema.addField(FieldDescriptor(name: "emails", number: 3, type: .string, isRepeated: true))

// Create and populate a message
var message = MessageFactory().createMessage(from: personSchema)
try message.set("Alice", forField: "name")
try message.set(Int32(25), forField: "age")
try message.set(["alice@example.com"], forField: "emails")

// Serialize to binary or JSON
// TypeRegistry is required for JSONSerializer and deserializers; empty registry is fine for scalar-only messages.
let registry = TypeRegistry()
let binaryData = try BinarySerializer().serialize(message)
let jsonData = try await JSONSerializer(options: .init(typeRegistry: registry)).serialize(message)

Working with Well-Known Types

// Timestamps
let timestampMessage = try DynamicMessage.timestampMessage(from: Date())
let backToDate = try timestampMessage.toDate()

// JSON-like structures
let data: [String: Any] = ["user": "john", "active": true]
let structMessage = try DynamicMessage.structMessage(from: data)

// Type erasure
let anyMessage = try message.packIntoAny()
let unpackedMessage = try await anyMessage.unpackFromAny(to: personSchema)

Features

  • Dynamic Message Creation: Create and manipulate protobuf messages at runtime
  • Schema Definition: Build message descriptors programmatically
  • Proto3 Compliance: Syntax tracking, zero defaults, optional presence, enum validation, unknown fields preservation
  • Canonical JSON: int64/uint64 as strings, bytes as base64, enums as names, includeDefaultValues
  • Nested Messages: Recursive binary serialization/deserialization with type resolution
  • Schema Evolution: Safe field addition/removal/renaming with unknown field round-trip
  • Typed Options: DescriptorOption enum for type-safe, Sendable descriptor options
  • Oneof Support: First-class OneofDescriptor with full bridge round-trip
  • Serialization: Binary and JSON serialization/deserialization
  • Well-Known Types: 18 types — Timestamp, Duration, Empty, FieldMask, Struct, Value, Any, ListValue, NullValue, and 9 wrapper types
  • Swift Protobuf Compatibility: Convert between static and dynamic messages, Visitor-based descriptor extraction
  • Type Registry: Centralized type management and lookup
  • Swift 6 Ready: All public types conform to Sendable

Examples

The library includes 47 working examples demonstrating various use cases:

git clone https://github.com/truewebber/swift-protoreflect.git
cd swift-protoreflect/examples

# Basic examples
swift run HelloWorld
swift run FieldTypes
swift run TimestampDemo

# Proto3 compliance
swift run SyntaxAndDefaults
swift run OptionalPresence
swift run UnknownFields
swift run JsonCanonical

# Advanced examples
swift run ApiGateway
swift run MessageTransform

Examples are organized by topic:

  • Basic Usage (4): Getting started
  • Dynamic Messages (7): Message manipulation
  • Serialization (5): Binary and JSON formats
  • Registry (4): Type management
  • Well-Known Types (10): Google standard types, wrapper types, ListValue
  • Advanced (6): Complex patterns
  • Real-World (5): Production scenarios
  • Proto3 Compliance (6): Syntax, optional presence, unknown fields, canonical JSON, schema evolution, nested messages

Requirements

  • Swift 5.9+
  • macOS 12.0+ / iOS 15.0+
  • Recommended: SwiftProtoReflect 6.0.0+

Dependencies

Documentation

Use Cases

  • Generic protobuf tools (viewers, debuggers, converters)
  • API gateways with dynamic message routing
  • Data processing pipelines with runtime schema handling
  • Testing tools that generate data for arbitrary schemas
  • Configuration systems using protobuf schemas

Integration with Swift Protobuf

SwiftProtoReflect works alongside existing Swift Protobuf code:

// Convert static to dynamic
let staticMessage = Person.with { /* ... */ }
let dynamicMessage = try staticMessage.toDynamicMessage()

// Convert dynamic to static
let backToStatic: Person = try dynamicMessage.toStaticMessage(as: Person.self)

Testing

The library has comprehensive test coverage covering all functionality and edge cases.

Quality metrics

Code coverage is measured with LLVM (make coverage after make test) over Sources/SwiftProtoReflect/:

| Metric | Coverage | |--------|----------| | Lines | 95.60% | | Functions | 95.79% | | Regions | 93.95% |

Figures reflect the current test suite; re-run make test and make coverage locally for up-to-date numbers.

License

MIT License. See LICENSE for details.

Package Metadata

Repository: truewebber/swift-protoreflect

Default branch: master

README: README.md