{"id":19680718,"url":"https://github.com/christophhagen/protobufcodable","last_synced_at":"2025-04-29T04:31:35.655Z","repository":{"id":108288451,"uuid":"404842583","full_name":"christophhagen/ProtobufCodable","owner":"christophhagen","description":"A binary encoder/decoder for Swift largely compatible with Google Protobuf","archived":false,"fork":false,"pushed_at":"2024-04-13T09:10:59.000Z","size":377,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T13:11:10.408Z","etag":null,"topics":["binary-data","codable","decoding","encoding","protocol-buffers","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/christophhagen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-09-09T19:15:46.000Z","updated_at":"2024-11-14T21:27:17.000Z","dependencies_parsed_at":"2023-07-05T10:15:22.360Z","dependency_job_id":null,"html_url":"https://github.com/christophhagen/ProtobufCodable","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophhagen%2FProtobufCodable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophhagen%2FProtobufCodable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophhagen%2FProtobufCodable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophhagen%2FProtobufCodable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/christophhagen","download_url":"https://codeload.github.com/christophhagen/ProtobufCodable/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251432844,"owners_count":21588664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary-data","codable","decoding","encoding","protocol-buffers","swift"],"created_at":"2024-11-11T18:05:51.525Z","updated_at":"2025-04-29T04:31:35.286Z","avatar_url":"https://github.com/christophhagen.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ProtobufCodable\n\nThis framework provides encoding and decoding of Swift `Codable` types, which is compatible with the [Google Protocol Buffer](https://developers.google.com/protocol-buffers) format.\n\n### Note\n\n`ProtobufCodable` only supports a subset of `Codable`, due to the limitations of Protocol Buffers. \nIf you're looking for a binary encoder with full support of all `Codable` features, have a look at [BinaryCodable](https://github.com/christophhagen/BinaryCodable).\n\n## Why?\n\nConsidering that Apple itself provides an implementation to use [Google Protocol Buffers in Swift](https://github.com/apple/swift-protobuf), why is there a need to provide another library for binary encoding/decoding with the same format?\n\nThe biggest advantage is that there is less work to do.\nWith [swift-protobuf](https://github.com/apple/swift-protobuf), you have to write (and understand) a `.proto` file, and install the `protoc` compiler with the Swift plugin.\nThen you have to generate the file, and integrate it into your code.\n\nUsing `ProtobufCodable` makes this much faster.\nConform your type to `Codable` (while respecting the [limitations](#supported-protobuf-types), and happily encode and decode `Protobuf`-compatible representations.\n\n#### Ensuring consistency\n\nEncoding formats for data exchange are meant to be stable, since sender and receiver may be using different platforms, programming languages, or software versions.\nThe Protobuf format is very aware of this fact, and the [documentation](https://protobuf.dev/programming-guides/dos-donts/) provides very useful pointers to minimize errors across versions.\nYou should be very careful when changing any `Codable` types used with `ProtobufCodable` to ensure that there are no decoding problems for older versions or stored data.\n\n### Why not?\n\nThere are instances when [swift-protobuf](https://github.com/apple/swift-protobuf) is the better choice:\n* **Speed**: As of now, `swift-protobuf` is about 30% faster for small messages than `ProtobufCodable`. Consider this if speed is an absolute priority.\n* **Consistency**: If you're already using `.proto` files within a project, it may be best to generate Swift code for them in order to guarantee consistency of the definitions.\n\n# Installation\n\n## Swift Package Manager\n\nSimply include in your `Package.swift`:\n```swift\ndependencies: [\n    .package(\n        name: \"ProtobufCodable\", \n        url: \"https://github.com/christophhagen/ProtobufCodable\", \n        from: \"1.0.0\")\n],\ntargets: [\n    .target(name: \"MyTarget\", dependencies: [\"ProtobufCodable\"])\n]\n```\n\n## Xcode project\n\nSelect your `Project`, navigate to the `Package Dependencies` tab, and add `https://github.com/christophhagen/ProtobufCodable` using the `+` button.\n\n# Usage\n\nWhile `ProtobufCodable` works with `Codable`, but not all features are supported, due to the `Protobuf` format being more limited.\nWhat follows is a description of all available `Protobuf` features, and how they are implemented in Swift.\n\n### Messages\n\nProtobuf `message`s are simply translated as Swift `struct`s of `class`es.\n\nThe proto definition\n\n```proto\nmessage MyType {\n    int64 value = 1;\n}\n```\n\nwould be equivalent to\n\n```swift\nstruct MyType: Codable {\n    let value: Int64\n    \n    enum CodingKeys: Int, CodingKey {\n        case value = 1\n    }\n}\n```\n\nThe first thing to note is the addition of the `CodingKeys` enum, which is how `Codable` specifies [integer keys](https://developer.apple.com/documentation/swift/codingkey) for properties.\nThe same [restrictions](https://protobuf.dev/programming-guides/proto3/#assigning) as with Protobuf field numbers apply:\n- The field number must be between `1` and `536,870,911`\n- The given number must be unique among all fields for that message.\n- Field numbers `19,000` to `19,999` are reserved for the Protocol Buffers implementation.\n\n### Supported Protobuf types\n\nThe type of the property must match one of the following:\n\n| Proto type | Swift type | Comment\n| :--- | :--- | :--- |\n| bool | Bool |\n| string | String |\n| bytes | Data |\n| double | Double | 64-bit\n| float | Float | 32-bit\n| int32 | Int32 | Preferred for positive values\n| int64 | Int64, Int | Preferred for positive values\n| uint32 | UInt32 |\n| uint64 | UInt64 |\n| sint32 | [Signed\\\u003cInt32\\\u003e](#signed-wrapper) | For positive and negative numbers\n| sint64 | [Signed\\\u003cInt64\\\u003e, Signed\\\u003cInt\\\u003e](#signed-wrapper) | For positive and negative numbers\n| fixed32 | [Fixed\\\u003cUInt32\\\u003e](#fixed-wrapper) | Always 4 byte\n| fixed64 | [Fixed\\\u003cUInt64\\\u003e, Fixed\\\u003cUInt\\\u003e](#fixed-wrapper) | Always 8 byte\n| sfixed32 | [Fixed\\\u003cInt32\\\u003e](#fixed-wrapper) | Always 4 byte\n| sfixed64 | [Fixed\\\u003cInt64\\\u003e, Fixed\\\u003cInt\\\u003e](#fixed-wrapper) | Always 8 byte\n| repeated | [Array](#arrays) |\n| oneof | [OneOf](#oneof) |\n| map | [Dictionary](#dictionary) |\n| enum | [Enum](#enum) | With integer raw values\n| any | - | Not supported\n\n### Signed wrapper\n\nThe `@Signed` property wrapper can be applied to properties to switch them from representing `int32` or `int64` protobuf types to `sint32` and `sint64`.\nThese types are more efficient when encoding negative numbers.\n\n```swift\nstruct MyType: Codable {\n\n    // Equivalent to `sint64`\n    @Signed\n    let value: Int64\n    \n    enum CodingKeys: Int, CodingKey {\n        case value = 1\n    }\n}\n```\n\n### Fixed wrapper\n\nSimilarly to `@Signed`, the `@Fixed` property wrapper converts integers to fixed-size format:\n\n```swift\nstruct MyType: Codable {\n\n    // Equivalent to `sfixed64`\n    @Fixed\n    let value: Int64\n    \n    enum CodingKeys: Int, CodingKey {\n        case value = 1\n    }\n}\n```\n\nThe wrapper converts the following types:\n\n| Swift type | Standard proto type | Fixed proto type\n| :--- | :--- | :--- |\n| UInt32 | uint32 | fixed32\n| UInt64, UInt | uint64 | fixed64\n| Int32 | int32 | sfixed32\n| Int64, Int | int64 | sfixed64\n\n### Arrays\n\nProtobuf `repeated` fields are equivalent to Swift `Array`s, although other types of sequences may also be used (like `Set`).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eProtobuf and Swift definitions\u003c/summary\u003e\n\n#### Protobuf\n\n```proto\nmessage MyMessage {\n\n    repeated int32 values = 1;\n}\n```\n\n#### Swift\n\n```swift\nstruct MyMessage {\n\n    var values: [Int32]\n\n    enum CodingKeys: Int, CodingKey {\n        case values = 1\n    }\n}\n```\n\n\u003c/details\u003e\n\n#### Packed arrays\n\nProtobuf 3 and `ProtobufCodable` use the [packed](https://protobuf.dev/programming-guides/encoding/#packed) format for repeated fields of primitive types (any [scalar type](https://protobuf.dev/programming-guides/proto2/#scalar) that is not `String` or `Data`).\n\nTo use unpacked repeated fields, use the `@PackedFalse` wrapper on an array, which is equivalent to the `[packed = false]` protobuf option.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eProtobuf and Swift definitions\u003c/summary\u003e\n\n#### Protobuf\n\n```proto\nmessage MyMessage {\n\n    repeated int32 values = 1 [packed=false];\n}\n```\n\n#### Swift\n\n```swift\nstruct MyMessage {\n\n    @PackedFalse\n    var values: [Int32]\n\n    enum CodingKeys: Int, CodingKey {\n        case values = 1\n    }\n}\n```\n\n\u003c/details\u003e\n\n### OneOf\n\nThe Protobuf [oneof](https://protobuf.dev/programming-guides/proto3/#oneof) type has no basic equivalent in Swift.\nThe desired behaviour can be reproduced by conforming an enum with associated values to the `OneOf` protocol.\n\n```proto\nmessage SampleMessage {\n    oneof selection {\n        string name = 4;\n        SubMessage sub_message = 9;\n    }\n}\n```\n\nThis is equivalent to:\n\n```swift\nstruct SampleMessage {\n\n    var oneof: Selection\n\n    enum Selection: OneOf {\n        case name(String)\n        case subMessage(SubMessage)\n\n        enum CodingKeys: Int, CodingKey {\n            case name = 4\n            case subMessage = 9\n        }\n    }\n\n    enum CodingKeys: Int, CodingKey {\n        case oneof = 123 // Irrelevant, not used\n    }\n}\n```\n\nNote that the `OneOf` protocol has no additional requirements, it is only used as an indicator to treat the type as a `OneOf`.\nIf the enum doesn't match the correct format, then an encoding error will be thrown.\n\n### Dictionary\n\n[Protobuf maps](https://protobuf.dev/programming-guides/proto3/#maps) are handled by Swift `Dictionary` types.\nFor a dictionary to be suitable, the `Key` has to be an integer, `Bool` , or `String`.\nThe `Value` can be any valid protobuf type, except another dictionary.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eProtobuf and Swift definitions\u003c/summary\u003e\n\n#### Protobuf\n\n```proto\nmessage MyMessage {\n\n    map\u003cstring, Project\u003e projects = 3;\n}\n```\n\n#### Swift\n\n```swift\nstruct MyMessage {\n\n    var projects: [String: Project]\n\n    enum CodingKeys: Int, CodingKey {\n        case projects = 3\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Enum\n\n[Protobuf enums](https://protobuf.dev/programming-guides/proto3/#enum) are represented as Swift enums with `RawValue` of type `Int`, `Int64`, or `Int32`.\nAccording to the protobuf spec, each enum must have a default case with `rawValue = 0`.\nAccording to the protobuf spec, enumerator constants must be in the range of a 32-bit integer.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eProtobuf and Swift definitions\u003c/summary\u003e\n\n#### Protobuf\n\n```proto\nenum Corpus {\n    CORPUS_UNSPECIFIED = 0;\n    CORPUS_UNIVERSAL = 1;\n    CORPUS_WEB = 2;\n    CORPUS_IMAGES = 3;\n    CORPUS_LOCAL = 4;\n    CORPUS_NEWS = 5;\n    CORPUS_PRODUCTS = 6;\n    CORPUS_VIDEO = 7;\n}\n\nmessage SearchRequest {\n    string query = 1;\n    int32 page_number = 2;\n    int32 results_per_page = 3;\n    Corpus corpus = 4;\n}\n```\n\n#### Swift\n\n```swift\n\nenum Corpus: Int, Codable {\n    case unspecified = 0\n    case universal = 1\n    case web = 2\n    case images = 3\n    case local = 4\n    case news = 5\n    case products = 6\n    case video = 7\n}\n\nstruct MyMessage {\n    var query: String\n    var pageNumber: Int32\n    var resultsPerPage: Int32\n    var corpus: Corpus\n\n    enum CodingKeys: Int, CodingKey {\n        case query = 1\n        case pageNumber = 2\n        case resultsPerPage = 3\n        case corpus = 4\n    }\n}\n```\n\n\u003c/details\u003e\n\n## Converting from/to data\n\nSimply import the module when you need to encode or decode a message:\n\n```swift\nimport ProtobufCodable\n```\n\n### Encoding\n\nConstruct an encoder when converting instances to binary data, and feed the message(s) into it:\n\n```swift\nlet message = Message(...)\n\nlet encoder = ProtobufEncoder()\nlet data = try encoder.encode(message)\n```\n\nIt's also possible to encode single values, arrays, optionals, sets, enums, and dictionaries, so long as they conform to `Codable`.\n\n### Decoding\n\nDecoding instances from binary data works much the same way:\n\n```swift\nlet decoder = ProtobufDecoder()\nlet message = decoder.decode(Message.self, from: data)\n```\n\nAlternatively, the type can be inferred:\n\n```swift\nlet message: Message = decoder.decode(from: data)\n```\n\n### Sorting keys\n\nThe `ProtobufEncoder` provides the `sortKeysDuringEncoding` option, which forces fields in \"keyed\" containers, such as `struct` properties (and some dictionaries), to be sorted in the binary data. \nThis sorting is done by using either the [integer keys](#coding-keys) (if defined), or the property names.\n\nSorting the binary data does not influence decoding, but introduces a computation penalty during encoding. \nIt should therefore only be used if the binary data must be consistent across multiple invocations.\n\n**Note:** The `sortKeysDuringEncoding` option does **not** guarantee deterministic binary data, and should be used with care.\nElements of any non-ordered types (Sets, Dictionaries) will appear in random order in the binary data.\nPlease also see the [Protobuf information](https://protobuf.dev/programming-guides/serialization-not-canonical/) on this topic.\n\n### Merging\n\nProtocol Buffers support the [merging of messages](https://developers.google.com/protocol-buffers/docs/encoding#optional), which overwrites non-repeated fields, and concatenates repeated fields.\n\n`ProtobufCodable` also supports this feature in most cases.\nJust encode the messages, and decode the joined data.\n\n### Errors\n\nIt is possible for both encoding and decoding to fail. \n\nAll possible errors occuring during encoding produce `EncodingError` errors, while unsuccessful decoding produces `DecodingError`s. See the documentation of the types to learn more about the different error conditions.\n\n## Roadmap\n\n### Generate protobuf definitions\n\nIt should be possible to generate a string containing a working Protobuf definition for any type that is determined to be Protobuf compatible.\nThis may be possible using `Mirror`, or by encoding one or more instances to figure out the structure.\n\n### Speed\n\nIncreasing the speed of the encoding and decoding process is not a huge priority at the moment. `ProtobufCodable` is about 30% slower than `swift-protobuf`, but still fast enough for most cases (`0.03ms` for encoding of a small object on a MacBook Air M1). If you have any pointers on how to improve the performance further, feel free to contribute.\n\n## Contributing\n\nUsers of the library are encouraged to contribute to this repository.\n\n### Feature suggestions\n\nPlease file an issue with a description of the feature you're missing. Check other open and closed issues for similar suggestions and comment on them before creating a new issue.\n\n### Bug reporting\n\nFile an issue with a clear description of the problem. Please include message definitions and other data where possible so that the error can be reproduced.\n\n### Documentation\n\nIf you would like to help translate the documentation of this library into other languages, please also open an issue, and I'll contact you for further discussions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophhagen%2Fprotobufcodable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchristophhagen%2Fprotobufcodable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophhagen%2Fprotobufcodable/lists"}