title: Basic Serialization sidebar_position: 1 id: basic-serialization license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
This page covers object graph serialization and core API usage in the default xlang mode for Fory Swift.
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)
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)
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.
BoolInt8, Int16, Int32, Int64, IntUInt8, UInt16, UInt32, UInt64, UIntFloat, DoubleStringDataInt and UInt keep 64-bit wire encodings on every platform. When a decoded value is outside the native range on a 32-bit target, deserialization throws ForyError.invalidData.
DateLocalDateDurationUse Date for timestamp values and LocalDate for day-only dates. LocalDate supports epoch-day and Date conversions through fromEpochDay(_:), toEpochDay(), init(utcDate:), and toUTCDate().
SerializerSerializer and are HashableSerializer, with Hashable keysChildren that use a separate serializer compose with:
OptionalSerializer<S>ArraySerializer<S>SetSerializer<S>DictionarySerializer<KS, VS>Any and AnyObjectAnyHashableAny 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.
The default xlang format is shared by all Fory implementations. The following sections cover its cross-language type mapping, type identity, and interoperability requirements.
Fory Swift can exchange payloads with other Fory implementations using the xlang protocol.
let fory = Fory()
@ForyStruct struct Order { var id: Int64 = 0 var amount: Double = 0 } let fory = Fory() try fory.register(Order.self, id: 100)
try fory.register(Order.self, name: "com.example.Order")
Swift Array<T> fields map to Fory list<T> unless field metadata explicitly requests dense array<T>. Use array<T> only for one-dimensional bool or numeric data.
| Fory schema | Swift field metadata sketch |
|---|---|
list<int32> | @ListField(element: .int32()) var ids: [Int32] |
array<bool> | @ArrayField(element: .bool) var flags: [Bool] |
array<int8> | @ArrayField(element: .int8) var values: [Int8] |
array<int16> | @ArrayField(element: .int16) var values: [Int16] |
array<int32> | @ArrayField(element: .int32()) var values: [Int32] |
array<int64> | @ArrayField(element: .int64()) var values: [Int64] |
array<uint8> | @ArrayField(element: .uint8) var values: [UInt8] |
array<uint16> | @ArrayField(element: .uint16) var values: [UInt16] |
array<uint32> | @ArrayField(element: .uint32()) var values: [UInt32] |
array<uint64> | @ArrayField(element: .uint64()) var values: [UInt64] |
array<float16> | @ArrayField(element: .float16) var values: [Float16] |
array<bfloat16> | @ArrayField(element: .bfloat16) var values: [BFloat16] |
array<float32> | @ArrayField(element: .float32) var values: [Float] |
array<float64> | @ArrayField(element: .float64) var values: [Double] |
An array that uses a separate element serializer still uses normal list encoding. Use @ArrayField only for supported dense bool or numeric arrays.
External structural serializers produce the same xlang STRUCT, ENUM, or UNION schema and value bytes as an equivalent ordinary Swift model:
@ForyStruct(target: ThirdParty.Order.self) struct OrderSerializer { var id: Int64 var amount: Double } try fory.register(OrderSerializer.self, id: 100)
Use .with(...) in field metadata and with: at a root. See External-Type Serialization.
That explicit selection is required because the structural serializer is a separate declaration. An external type with one intentional retroactive Target == Self conformance instead uses ordinary roots, fields, and carriers.
Swift has no native serialization mode. A known @ForyUnion case has zero or one associated value. Use a struct payload for a union alternative with multiple logical fields.
Generate Swift models directly from Fory IDL/Proto/FBS inputs:
foryc schema.fdl --swift_out ./Sources/Generated
Generated Swift code includes:
@ForyStruct, @ForyEnum, @ForyUnion, and field/case metadataForyModule.install(_:) helpers with transitive import installationtoBytes / fromBytes helpers on generated typesInstall the generated module before xlang serialization:
let fory = Fory(ref: true) try Addressbook.ForyModule.install(fory) let payload = try fory.serialize(book) let decoded: Addressbook.AddressBook = try fory.deserialize(payload)
cd integration_tests/idl_tests ./run_swift_tests.sh
This runs Swift roundtrip matrix tests and Java peer roundtrip checks (IDL_PEER_LANG=swift).
Enable debug output when running xlang tests:
ENABLE_FORY_DEBUG_OUTPUT=1 FORY_SWIFT_JAVA_CI=1 mvn -T16 test -Dtest=org.apache.fory.xlang.SwiftXlangTest
import Fory @ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) let data = try fory.serialize(person) let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)")
For more cross-language rules and examples, see: