TLV8 (Type-Length-Value) Coder library, compatible with Embedded Swift.
Types are encoded and decoded explicitly via TLVCodable — either with conformances generated by the @TLVCodable macro, or written by hand. The library does not depend on Codable runtime machinery or Foundation, so it can be used on bare-metal targets.
import TLVCoding @TLVCodable struct ProvisioningState: Equatable { var state: State var result: Result enum CodingKeys: UInt8, TLVCodingKey { case state = 0x01 case result = 0x02 } } let value = ProvisioningState(state: .provisioning, result: .success) let data = value.tlvData // Data([0x01, 0x01, 0x01, 0x02, 0x01, 0x01]) let decoded = ProvisioningState(tlvData: data)
If no CodingKeys enum is declared, one is generated with sequential type codes in property declaration order.
The same conformance can be written manually (e.g. for Embedded Swift or custom binary layouts):
extension ProvisioningState: TLVCodable { init?(tlvData: Data) { guard let container = TLVContainer(data: tlvData), let state = container.decode(State.self, forKey: CodingKeys.state), let result = container.decode(Result.self, forKey: CodingKeys.result) else { return nil } self.state = state self.result = result } var tlvData: Data { var container = TLVContainer() container.encode(state, forKey: CodingKeys.state) container.encode(result, forKey: CodingKeys.result) return container.data } }
Types with a custom binary layout can skip TLVContainer entirely and produce their payload directly:
extension Version: TLVCodable { init?(tlvData: Data) { guard tlvData.count == 3 else { return nil } self.major = tlvData[0] self.minor = tlvData[1] self.patch = tlvData[2] } var tlvData: Data { Data([major, minor, patch]) } }
On Swift 6.2+ toolchains the library integrates swift-binary-parsing: TLVContainer.init(data:) is backed by its safe, bounds-checked parser, and TLVTypeCode, TLVItem and TLVContainer conform to ExpressibleByParsing for throwing, span-based parsing:
import BinaryParsing import TLVCoding // parse a whole container let container = try TLVContainer(parsing: data) // or consume items from a ParserSpan try data.withParserSpan { input in let item = try TLVItem(parsing: &input) ... }
The dependency raises the Apple platform deployment targets (macOS 13, iOS 16, watchOS 9, tvOS 16) and can be skipped with the SWIFTPM_ENABLE_BINARY_PARSING environment variable:
SWIFTPM_ENABLE_BINARY_PARSING=0 swift build
Macros are disabled under Embedded Swift (write conformances by hand) and the swift-syntax dependency can be skipped entirely with the SWIFTPM_ENABLE_MACROS environment variable:
SWIFTPM_ENABLE_MACROS=0 SWIFTPM_ENABLE_BINARY_PARSING=0 \
swift build --target TLVCoding \
--triple armv7em-none-none-eabi \
-Xswiftc -enable-experimental-feature -Xswiftc Embedded \
-Xswiftc -wmoOn platforms without Foundation, a minimal Data type is provided by the library.
- CoreModel - Swift ORM with the same explicit coding and macro design
- BluetoothLinux - Pure Swift Linux Bluetooth Stack
- GATT - Bluetooth Generic Attribute Profile (GATT) for Swift