Basic Serialization
This page covers object graph serialization and core API usage in Swift.
Object Graph Serialization
Use @ForyStruct, @ForyEnum, or @ForyUnion, register types, then serialize and deserialize.
import Foundation
import Fory
@ForyStruct
struct Address: Equatable {
var street: String = ""
var zip: Int32 = 0
}
@ForyStruct
struct Person: Equatable {
var id: Int64 = 0
var name: String = ""
var nickname: String? = nil
var tags: Set<String> = []
var scores: [Int32] = []
var addresses: [Address] = []
var metadata: [Int8: Int32?] = [:]
}
let fory = Fory()
try fory.register(Address.self, id: 100)
try fory.register(Person.self, id: 101)
let person = Person(
id: 42,
name: "Alice",
nickname: nil,
tags: ["swift", "xlang"],
scores: [10, 20, 30],
addresses: [Address(street: "Main", zip: 94107)],
metadata: [1: 100, 2: nil]
)
let data = try fory.serialize(person)
let decoded: Person = try fory.deserialize(data)
assert(decoded == person)
Working with Existing Buffers
Append serialized bytes to an existing Data and deserialize from ByteBuffer.
var output = Data()
try fory.serialize(person, to: &output)
let inputBuffer = ByteBuffer(data: output)
let fromBuffer: Person = try fory.deserialize(from: inputBuffer)
assert(fromBuffer == person)
Selecting a Serializer
A type that implements Serializer with Target == Self selects itself:
let data = try fory.serialize(person)
let decoded: Person = try fory.deserialize(data)
This implicit selection composes through generated fields and ordinary optionals, arrays, sets, and dictionaries. It also applies when an application intentionally gives an external type one retroactive self-target conformance.
When a separate serializer targets the value, select it with with:
try fory.register(UserSerializer.self, id: 200)
let data = try fory.serialize(
externalUser,
with: UserSerializer.self
)
let decoded = try fory.deserialize(
data,
with: UserSerializer.self
)
The same selection works with existing buffers:
var output = Data()
try fory.serialize(
externalUser,
with: UserSerializer.self,
to: &output
)
let input = ByteBuffer(data: output)
let decoded = try fory.deserialize(
from: input,
with: UserSerializer.self
)
See External-Type Serialization for structural serializers and recursive carrier roots. See Custom Serializers for serializers implemented directly by a type, retroactive conformances, and separate custom serializers.
Built-in Supported Types
Primitive and scalar
BoolInt8,Int16,Int32,Int64,IntUInt8,UInt16,UInt32,UInt64,UIntFloat,DoubleStringData
Date and time
DateLocalDateDuration
Use Date for timestamp values and LocalDate for day-only dates. LocalDate
supports epoch-day and Date conversions through fromEpochDay(_:),
toEpochDay(), init(utcDate:), and toUTCDate().
Collections
- Optionals and arrays whose values directly implement
Serializer - Sets whose elements directly implement
Serializerand areHashable - Dictionaries whose keys and values directly implement
Serializer, withHashablekeys
Children that use a separate serializer compose with:
OptionalSerializer<S>ArraySerializer<S>SetSerializer<S>DictionarySerializer<KS, VS>
Dynamic
AnyandAnyObjectAnyHashable- Arbitrary application protocol values
- Supported heterogeneous arrays and dictionaries
Any and AnyObject roots use direct root APIs. Arbitrary application
protocol roots and dynamic values nested in carriers use explicit with:
selection.
See Polymorphism and Dynamic Types.