Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

CBOR

Swift 6.0PlatformsLicensemacOSLinuxWindowsDocumentation

CBOR is a lightweight implementation of the CBOR (Concise Binary Object Representation) format in Swift. It allows you to encode and decode data to and from the CBOR format, work directly with the CBOR data model, and integrate with Swift's Codable protocol.

Features

  • Direct CBOR Data Model:
    Represent CBOR values using an enum with cases for unsigned/negative integers, byte strings, text strings, arrays, maps (ordered key/value pairs), tagged values, simple values, booleans, null, undefined, and floats.

  • Memory-Optimized for Embedded Swift:
    Uses ArraySlice<UInt8> internally to avoid heap allocations by referencing original data instead of copying. Includes zero-copy access methods and memory-efficient iterators for arrays and maps.

  • Encoding & Decoding:
    Easily convert between CBOR values and byte arrays.

  • Full Codable Support:
    Use CBOREncoder and CBORDecoder for complete support of Swift's Codable protocol, including:

    • Single value encoding/decoding
    • Keyed containers (for dictionaries and objects)
    • Unkeyed containers (for arrays)
    • Nested containers
    • Custom encoding/decoding
    • Sets and other collection types
    • Optionals and deeply nested optionals
    • Non-String dictionary keys
    • Cross-platform date handling (ISO8601 format on Apple platforms)
  • Error Handling:
    Detailed error types (CBORError) to help you diagnose encoding/decoding issues.

Table of Contents

Documentation

Comprehensive documentation is available via DocC:

  • Online Documentation
  • Generate locally with: swift package --allow-writing-to-directory ./docs generate-documentation --target CBOR

Installation

Swift Package Manager

Add the CBOR package to your Swift package dependencies:

// swift-tools-version:6.0
import PackageDescription
letpackage=Package(
name:"YourProject",
dependencies:[.package(url:"https://github.com/edgeengineer/cbor", from:"0.0.4")],
targets:[.target(
name:"YourTarget",
dependencies:[.product(name:"CBOR",package:"cbor")])])

Quick Start

1. Working Directly with CBOR Values

import CBOR
// Create a CBOR value (an unsigned integer)
letcborValue:CBOR=.unsignedInt(42)
// Encode the CBOR value to a byte array
letencodedBytes= cborValue.encode()print("Encoded bytes:", encodedBytes)
// Decode the bytes back into a CBOR value
do{letdecodedValue=tryCBOR.decode(encodedBytes)print("Decoded CBOR value:", decodedValue)}catch{print("Decoding error:", error)}

2. Using Codable

import CBOR
// Define your data structures
structPerson:Codable{letname:Stringletage:Intletaddresses:[Address]letmetadata:[String:String]}structAddress:Codable{letstreet:Stringletcity:String}
// Create an instance
letperson=Person(
name:"Alice",
age:30,
addresses:[Address(street:"123 Main St", city:"Wonderland"),Address(street:"456 Side Ave", city:"Fantasialand")],
metadata:["occupation":"Engineer","department":"R&D"])
// Encode to CBOR
do{letencoder=CBOREncoder()letcborData=try encoder.encode(person)print("Encoded CBOR Data:", cborData asNSData)
// Decode back from CBOR
letdecoder=CBORDecoder()letdecodedPerson=try decoder.decode(Person.self, from: cborData)print("Decoded Person:", decodedPerson)}catch{print("Error:", error)}

3. Working with Complex CBOR Structures

// Create an array of CBOR values
letarrayCBOR:CBOR=.array([.unsignedInt(1),.textString("hello"),.bool(true)])
// Create a map (ordered key/value pairs)
letmapCBOR:CBOR=.map([CBORMapPair(key:.textString("name"), value:.textString("SwiftCBOR")),CBORMapPair(key:.textString("version"), value:.unsignedInt(1))])
// Combine them into a nested structure
letnestedCBOR:CBOR=.map([CBORMapPair(key:.textString("data"), value: arrayCBOR),CBORMapPair(key:.textString("info"), value: mapCBOR)])

4. Error Handling

do{letcbor=tryCBOR.decode([0xff,0x00]) // Example invalid CBOR data
}catchlet error as CBORError{switch error {case.invalidCBOR:print("Invalid CBOR data")case.typeMismatch(let expected,let actual):print("Type mismatch: expected \(expected), found \(actual)")case.prematureEnd:print("Unexpected end of data")default:print("Other CBOR error:", error.description)}}catch{print("Unexpected error:", error)}

5. Advanced Codable Examples

// Example of nested containers and arrays
structTeam:Codable{letname:Stringletmembers:[Member]letstats:Statisticslettags:Set<String>}structMember:Codable{letid:Intletname:Stringletroles:[Role]enumRole:String,Codable{case developer
case designer
case manager
}}structStatistics:Codable{letprojectsCompleted:IntletaverageRating:DoubleletactiveYears:[Int]}
// Create and encode a team
letteam=Team(
name:"Dream Team",
members:[Member(id:1, name:"Alice", roles:[.developer,.manager]),Member(id:2, name:"Bob", roles:[.designer])],
stats:Statistics(
projectsCompleted:12,
averageRating:4.8,
activeYears:[2020,2021,2022]),
tags:["innovative","agile","productive"])letencoder=CBOREncoder()letcborData=try encoder.encode(team)

6. Working with Sets

import CBOR
// Define a struct with Set properties
structSetContainer:Codable,Equatable{letstringSet:Set<String>letintSet:Set<Int>}
// Create an instance with sets
letsetExample=SetContainer(
stringSet:Set(["apple","banana","cherry"]),
intSet:Set([1,2,3,4,5]))
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(setExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(SetContainer.self, from: encoded)
// Verify sets are preserved
assert(decoded.stringSet.contains("apple"))assert(decoded.intSet.contains(3))

7. Working with Optionals and Nested Optionals

import CBOR
// Define a struct with optional and nested optional properties
structOptionalExample:Codable,Equatable{letsimpleOptional:String?letnestedOptional:Int??letoptionalArray:[Double?]?letoptionalDict:[String:Bool?]?}
// Create an instance with various optional values
letoptionalExample=OptionalExample(
simpleOptional:"present",
nestedOptional:nil,
optionalArray:[1.0,nil,3.0],
optionalDict:["yes":true,"no":false,"maybe":nil])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(optionalExample)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(OptionalExample.self, from: encoded)
// Verify optionals are preserved
assert(decoded.simpleOptional =="present")assert(decoded.nestedOptional ==nil)assert(decoded.optionalArray?[1]==nil)assert(decoded.optionalDict?["maybe"]==nil)

8. Non-String Dictionary Keys

import CBOR
// Define an enum to use as dictionary keys
enumColor:String,Codable,Hashable{case red
case green
case blue
}structEnumKeyDict:Codable,Equatable{letcolorValues:[Color:Int]}
// Create an instance with enum keys
letcolorDict=EnumKeyDict(colorValues:[.red:1,.green:2,.blue:3])
// Encode to CBOR
letencoder=CBOREncoder()letencoded=try encoder.encode(colorDict)
// Decode from CBOR
letdecoder=CBORDecoder()letdecoded=try decoder.decode(EnumKeyDict.self, from: encoded)
// Verify dictionary with enum keys is preserved
assert(decoded.colorValues[.red]==1)assert(decoded.colorValues[.green]==2)assert(decoded.colorValues[.blue]==3)

Memory-Efficient Usage (Embedded Swift)

This CBOR library is optimized for memory-constrained environments like Embedded Swift. It uses ArraySlice<UInt8> internally to avoid unnecessary heap allocations by referencing original data instead of copying it.

9. Working with Byte Strings (Zero-Copy)

import CBOR
// Create a byte string from raw data
letrawData:[UInt8]=[0x01,0x02,0x03,0x04,0x05]letcbor=CBOR.byteString(ArraySlice(rawData))
// Zero-copy access (recommended for Embedded Swift)
iflet slice = cbor.byteStringSlice(){print("Length: \(slice.count)")print("First byte: 0x\(String(slice.first!, radix:16))")
// Process bytes without copying
forbytein slice {print("Byte: 0x\(String(byte, radix:16))")}}
// Copy to Array only when needed (allocates memory)
iflet bytes = cbor.byteStringValue(){lethexString= bytes.map{String(format:"%02x", $0)}.joined()print("Hex: \(hexString)")}

10. Working with Text Strings (Zero-Copy UTF-8)

import CBOR
// Create a text string with Unicode content
lettext="Hello, 世界! 🌍"letcbor=CBOR.textString(ArraySlice(text.utf8))
// Zero-copy access to UTF-8 bytes
iflet slice = cbor.textStringSlice(){print("UTF-8 byte count: \(slice.count)")
// Convert to String without intermediate allocation
iflet string =String(bytes: slice, encoding:.utf8){print("Text: \(string)")}
// Or examine raw UTF-8 bytes
forbytein slice {print("UTF-8 byte: 0x\(String(byte, radix:16))")}}
// Convenience method for direct String conversion
iflet text = cbor.stringValue {print("Decoded text: \(text)")}

11. Memory-Efficient Array Iteration

import CBOR
// Decode CBOR data containing an array
letencodedArray:[UInt8]=[0x83,0x01,0x62,0x68,0x69,0xf5] // [1, "hi", true]
letcbor=tryCBOR.decode(encodedArray)
// Use iterator to avoid loading entire array into memory
iflet iterator =try cbor.arrayIterator(){variterator= iterator // Make mutable
varindex=0whilelet element = iterator.next(){print("Element \(index):")switch element {case.unsignedInt(let value):print(" Integer: \(value)")case.textString:
// Use zero-copy access for strings
iflet text = element.stringValue {print(" Text: \(text)")}case.bool(let flag):print(" Boolean: \(flag)")default:print(" Other: \(element)")}
index +=1}}
// Compare with traditional approach (allocates full array)
iflet elements =try cbor.arrayValue(){print("Traditional approach loaded \(elements.count) elements into memory")}

12. Memory-Efficient Map Iteration

import CBOR
// Decode CBOR data containing a map
letencodedMap:[UInt8]=[0xa2,0x64,0x6e,0x61,0x6d,0x65,0x64,0x4a,0x6f,0x68,0x6e,0x63,0x61,0x67,0x65,0x18,0x1e]
// {"name": "John", "age": 30}
letcbor=tryCBOR.decode(encodedMap)
// Use iterator to process key-value pairs without loading entire map
iflet iterator =try cbor.mapIterator(){variterator= iterator // Make mutable
whilelet pair = iterator.next(){print("Processing key-value pair:")
// Handle the key (zero-copy for strings)
iflet keyText = pair.key.stringValue {print(" Key: \(keyText)")}
// Handle the value
switch pair.value {case.unsignedInt(let value):print(" Value: \(value)")case.textString:iflet valueText = pair.value.stringValue {print(" Value: \(valueText)")}default:print(" Value: \(pair.value)")}}}
// Compare with traditional approach (allocates full map)
iflet pairs =try cbor.mapValue(){print("Traditional approach loaded \(pairs.count) pairs into memory")}

13. Performance Comparison: Slice vs Value Methods

import CBOR
// Create a large byte string
letlargeData=[UInt8](repeating:0xFF, count:10000)letcbor=CBOR.byteString(ArraySlice(largeData))
// ✅ Memory-efficient: Zero-copy access
iflet slice = cbor.byteStringSlice(){
// No memory allocation - just references original data
letsum= slice.reduce(0,+)print("Sum using slice: \(sum)")}
// ⚠️ Memory-intensive: Copies data
iflet bytes = cbor.byteStringValue(){
// Allocates 10KB of memory for the copy
letsum= bytes.reduce(0,+)print("Sum using copy: \(sum)")}

14. Decoding from Original Data

import CBOR
// When you decode CBOR from external data
letnetworkData:[UInt8]=[0x65,0x48,0x65,0x6c,0x6c,0x6f] // "Hello"
letcbor=tryCBOR.decode(networkData)
// The decoded CBOR references the original networkData
iflet slice = cbor.textStringSlice(){
// slice points into networkData - no copying!
print("Text length: \(slice.count)")
// As long as networkData stays alive, slice is valid
iflet text =String(bytes: slice, encoding:.utf8){print("Decoded: \(text)")}}

Memory Usage Guidelines

  • Prefer slice methods (byteStringSlice(), textStringSlice()) over value methods for better memory efficiency
  • Use iterators (arrayIterator(), mapIterator()) for large collections to avoid loading everything into memory
  • Keep original data alive when using slices, as they reference the original data
  • Use stringValue convenience property for direct String conversion without intermediate allocations

Platform Compatibility

This CBOR library is designed to work across all Swift-supported platforms:

  • Apple platforms (macOS, iOS, tvOS, watchOS, visionOS): Full feature support including ISO8601 date formatting
  • Linux: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Windows: Full feature support except ISO8601 date formatting (dates are still supported through other formats)
  • Android: Cross-platform compatibility maintained

Date Handling Notes

The library provides automatic date encoding/decoding support through the Codable interface:

  • On Apple platforms: Dates are automatically formatted using ISO8601DateFormatter when encoded as text strings
  • On Linux/Windows: Date text string formatting is not available, but dates can still be encoded/decoded using other CBOR representations (tagged values, numeric timestamps, etc.)

This ensures your code remains fully functional across all platforms while taking advantage of platform-specific optimizations where available.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

A CBOR library for Cross Platform Swift Projects

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages