This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new a9d172084 feat(swift): dynamic any serializer for polymorphism (#3368)
a9d172084 is described below

commit a9d172084062dc2fc07efa50eaa280c8463aad0e
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Feb 19 19:27:15 2026 +0800

    feat(swift): dynamic any serializer for polymorphism (#3368)
    
    ## Why?
    
    Swift xlang polymorphism currently relies on a custom `AnyAnimal` bridge
    in the peer, which makes dynamic payload handling narrow and hard to
    extend. This PR adds first-class dynamic `Any` serialization so
    polymorphic lists/maps can round-trip through the core runtime and
    macro-generated models directly.
    
    
    ## What does this PR do?
    
    - Add `AnySerializer.swift` with runtime `Any` codec support, including
    dynamic type info read/write, null sentinel handling, and dynamic
    payload encode/decode helpers.
    - Add dynamic container helpers for `[Any]`, `[String: Any]`, and
    `[Int32: Any]` (including map key-type probing for dynamic map decode).
    - Expose `WriteContext`/`ReadContext` convenience APIs and new `Fory`
    overloads for serializing/deserializing dynamic Any collections in both
    buffer and stream-style entry points.
    - Extend `TypeResolver` to parse dynamic wire type metadata and decode
    dynamic values (primitive/array/list/map/registered types) by id or
    name, with registration-mode tracking.
    - Add `RefReader.readRefValue(_:)` support used by dynamic Any reference
    resolution.
    - Extend `@ForyObject` macro generation to support `Any`, `[Any]`,
    `[String: Any]`, and `[Int32: Any]` fields with generated read/write
    paths and compatibility metadata handling; reject unsupported `Set<Any>`
    and unsupported dictionary key types.
    - Simplify `ForySwiftXlangPeer` polymorphic handlers by removing the
    custom `AnyAnimal` codec and switching to native `[Any]` / `[String:
    Any]` round-trips.
    
    
    ## Related issues
    
    Closes #3367
    #1017 #3349
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 swift/Sources/ForySwift/AnySerializer.swift        | 471 +++++++++++++++++++++
 swift/Sources/ForySwift/Context.swift              | 108 +++++
 swift/Sources/ForySwift/Fory.swift                 | 417 ++++++++++++++++++
 swift/Sources/ForySwift/RefResolver.swift          |   8 +
 swift/Sources/ForySwift/TypeResolver.swift         | 246 +++++++++++
 .../Sources/ForySwiftMacros/ForyObjectMacro.swift  | 241 ++++++++++-
 swift/Sources/ForySwiftXlangPeer/main.swift        | 248 +----------
 swift/Tests/ForySwiftTests/ForySwiftTests.swift    | 165 ++++++++
 8 files changed, 1654 insertions(+), 250 deletions(-)

diff --git a/swift/Sources/ForySwift/AnySerializer.swift 
b/swift/Sources/ForySwift/AnySerializer.swift
new file mode 100644
index 000000000..c98f5968f
--- /dev/null
+++ b/swift/Sources/ForySwift/AnySerializer.swift
@@ -0,0 +1,471 @@
+// 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
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import Foundation
+
+private enum DynamicAnyMapHeader {
+    static let keyNull: UInt8 = 0b0000_0010
+    static let declaredKeyType: UInt8 = 0b0000_0100
+    static let valueNull: UInt8 = 0b0001_0000
+}
+
+public struct ForyAnyNullValue: Serializer {
+    public init() {}
+
+    public static func foryDefault() -> ForyAnyNullValue {
+        ForyAnyNullValue()
+    }
+
+    public static var staticTypeId: ForyTypeId {
+        .none
+    }
+
+    public var foryIsNone: Bool {
+        true
+    }
+
+    public func foryWriteData(_ context: WriteContext, hasGenerics: Bool) 
throws {
+        _ = context
+        _ = hasGenerics
+    }
+
+    public static func foryReadData(_ context: ReadContext) throws -> 
ForyAnyNullValue {
+        _ = context
+        return ForyAnyNullValue()
+    }
+}
+
+private protocol OptionalTypeMarker {
+    static var noneValue: Self { get }
+}
+
+extension Optional: OptionalTypeMarker {
+    static var noneValue: Optional<Wrapped> { nil }
+}
+
+private struct DynamicAnyValue: Serializer {
+    var value: Any = ForyAnyNullValue()
+
+    init(_ value: Any) {
+        self.value = value
+    }
+
+    static func foryDefault() -> DynamicAnyValue {
+        DynamicAnyValue(ForyAnyNullValue())
+    }
+
+    static var staticTypeId: ForyTypeId {
+        .unknown
+    }
+
+    static var isNullableType: Bool {
+        true
+    }
+
+    static var isReferenceTrackableType: Bool {
+        true
+    }
+
+    var foryIsNone: Bool {
+        value is ForyAnyNullValue
+    }
+
+    static func wrapped(_ value: Any?) -> DynamicAnyValue {
+        guard let value else {
+            return .foryDefault()
+        }
+        guard let unwrapped = unwrapOptionalAny(value) else {
+            return .foryDefault()
+        }
+        if unwrapped is NSNull {
+            return .foryDefault()
+        }
+        return DynamicAnyValue(unwrapped)
+    }
+
+    func anyValue() -> Any? {
+        foryIsNone ? nil : value
+    }
+
+    func anyValueForCollection() -> Any {
+        foryIsNone ? NSNull() : value
+    }
+
+    func foryWriteData(_ context: WriteContext, hasGenerics: Bool) throws {
+        if foryIsNone {
+            return
+        }
+        try writeAnyPayload(value, context: context, hasGenerics: hasGenerics)
+    }
+
+    static func foryReadData(_ context: ReadContext) throws -> DynamicAnyValue 
{
+        guard let typeInfo = context.dynamicTypeInfo(for: Self.self) else {
+            throw ForyError.invalidData("dynamic Any value requires type info")
+        }
+        context.clearDynamicTypeInfo(for: Self.self)
+        if typeInfo.wireTypeID == .none {
+            return .foryDefault()
+        }
+        return DynamicAnyValue(try 
context.typeResolver.readDynamicValue(typeInfo: typeInfo, context: context))
+    }
+
+    static func foryWriteTypeInfo(_ context: WriteContext) throws {
+        _ = context
+        throw ForyError.invalidData("dynamic Any value type info is 
runtime-only")
+    }
+
+    func foryWriteTypeInfo(_ context: WriteContext) throws {
+        if foryIsNone {
+            context.writer.writeUInt8(UInt8(truncatingIfNeeded: 
ForyTypeId.none.rawValue))
+            return
+        }
+        try writeAnyTypeInfo(value, context: context)
+    }
+
+    static func foryReadTypeInfo(_ context: ReadContext) throws {
+        let typeInfo = try context.typeResolver.readDynamicTypeInfo(context: 
context)
+        context.setDynamicTypeInfo(for: Self.self, typeInfo)
+    }
+
+    func foryWrite(
+        _ context: WriteContext,
+        refMode: RefMode,
+        writeTypeInfo: Bool,
+        hasGenerics: Bool
+    ) throws {
+        if refMode != .none {
+            if foryIsNone {
+                context.writer.writeInt8(RefFlag.null.rawValue)
+                return
+            }
+            if refMode == .tracking, anyValueIsReferenceTrackable(value), let 
object = value as AnyObject? {
+                if context.refWriter.tryWriteReference(writer: context.writer, 
object: object) {
+                    return
+                }
+            } else {
+                context.writer.writeInt8(RefFlag.notNullValue.rawValue)
+            }
+        }
+
+        if writeTypeInfo {
+            try foryWriteTypeInfo(context)
+        }
+        try foryWriteData(context, hasGenerics: hasGenerics)
+    }
+
+    static func foryRead(
+        _ context: ReadContext,
+        refMode: RefMode,
+        readTypeInfo: Bool
+    ) throws -> DynamicAnyValue {
+        if refMode != .none {
+            let rawFlag = try context.reader.readInt8()
+            guard let flag = RefFlag(rawValue: rawFlag) else {
+                throw ForyError.refError("invalid ref flag \(rawFlag)")
+            }
+
+            switch flag {
+            case .null:
+                return .foryDefault()
+            case .ref:
+                let refID = try context.reader.readVarUInt32()
+                let referenced = try context.refReader.readRefValue(refID)
+                if let value = referenced as? DynamicAnyValue {
+                    return value
+                }
+                if referenced is NSNull {
+                    return .foryDefault()
+                }
+                return DynamicAnyValue(referenced)
+            case .refValue:
+                let reservedRefID = context.refReader.reserveRefID()
+                context.pushPendingReference(reservedRefID)
+                if readTypeInfo {
+                    try foryReadTypeInfo(context)
+                }
+                let value = try foryReadData(context)
+                context.finishPendingReferenceIfNeeded(value)
+                context.popPendingReference()
+                return value
+            case .notNullValue:
+                break
+            }
+        }
+
+        if readTypeInfo {
+            try foryReadTypeInfo(context)
+        }
+        return try foryReadData(context)
+    }
+}
+
+private func unwrapOptionalAny(_ value: Any) -> Any? {
+    let mirror = Mirror(reflecting: value)
+    guard mirror.displayStyle == .optional else {
+        return value
+    }
+    guard let (_, child) = mirror.children.first else {
+        return nil
+    }
+    return child
+}
+
+private func anyValueIsReferenceTrackable(_ value: Any) -> Bool {
+    guard let serializer = value as? any Serializer else {
+        return false
+    }
+    return type(of: serializer).isReferenceTrackableType
+}
+
+private func writeAnyTypeInfo(_ value: Any, context: WriteContext) throws {
+    if let serializer = value as? any Serializer {
+        try serializer.foryWriteTypeInfo(context)
+        return
+    }
+
+    if value is [Any] {
+        context.writer.writeUInt8(UInt8(truncatingIfNeeded: 
ForyTypeId.list.rawValue))
+        return
+    }
+    if value is [String: Any] || value is [Int32: Any] {
+        context.writer.writeUInt8(UInt8(truncatingIfNeeded: 
ForyTypeId.map.rawValue))
+        return
+    }
+
+    throw ForyError.invalidData("unsupported dynamic Any runtime type 
\(type(of: value))")
+}
+
+private func writeAnyPayload(_ value: Any, context: WriteContext, hasGenerics: 
Bool) throws {
+    if let serializer = value as? any Serializer {
+        try serializer.foryWriteData(context, hasGenerics: hasGenerics)
+        return
+    }
+    if let list = value as? [Any] {
+        try writeAnyList(list, context: context, refMode: .none, hasGenerics: 
hasGenerics)
+        return
+    }
+    if let map = value as? [String: Any] {
+        // Always include key type info for dynamic map payload.
+        try writeStringAnyMap(map, context: context, refMode: .none, 
hasGenerics: false)
+        return
+    }
+    if let map = value as? [Int32: Any] {
+        // Always include key type info for dynamic map payload.
+        try writeInt32AnyMap(map, context: context, refMode: .none, 
hasGenerics: false)
+        return
+    }
+    throw ForyError.invalidData("unsupported dynamic Any runtime type 
\(type(of: value))")
+}
+
+public func castAnyDynamicValue<T>(_ value: Any?, to type: T.Type) throws -> T 
{
+    _ = type
+    if value == nil {
+        if T.self == Any.self {
+            return ForyAnyNullValue() as! T
+        }
+        if T.self == AnyObject.self {
+            return NSNull() as! T
+        }
+        if T.self == (any Serializer).self {
+            return ForyAnyNullValue() as! T
+        }
+        if let optionalType = T.self as? any OptionalTypeMarker.Type {
+            return optionalType.noneValue as! T
+        }
+    }
+
+    guard let typed = value as? T else {
+        throw ForyError.invalidData("cannot cast dynamic Any value to \(type)")
+    }
+    return typed
+}
+
+public func writeAny(
+    _ value: Any?,
+    context: WriteContext,
+    refMode: RefMode,
+    writeTypeInfo: Bool = true,
+    hasGenerics: Bool = false
+) throws {
+    try DynamicAnyValue.wrapped(value).foryWrite(
+        context,
+        refMode: refMode,
+        writeTypeInfo: writeTypeInfo,
+        hasGenerics: hasGenerics
+    )
+}
+
+public func readAny(
+    context: ReadContext,
+    refMode: RefMode,
+    readTypeInfo: Bool = true
+) throws -> Any? {
+    try DynamicAnyValue.foryRead(context, refMode: refMode, readTypeInfo: 
readTypeInfo).anyValue()
+}
+
+public func writeAnyList(
+    _ value: [Any]?,
+    context: WriteContext,
+    refMode: RefMode,
+    writeTypeInfo: Bool = false,
+    hasGenerics: Bool = true
+) throws {
+    let wrapped = value?.map { DynamicAnyValue.wrapped($0) }
+    try wrapped.foryWrite(
+        context,
+        refMode: refMode,
+        writeTypeInfo: writeTypeInfo,
+        hasGenerics: hasGenerics
+    )
+}
+
+public func readAnyList(
+    context: ReadContext,
+    refMode: RefMode,
+    readTypeInfo: Bool = false
+) throws -> [Any]? {
+    let wrapped: [DynamicAnyValue]? = try [DynamicAnyValue]?.foryRead(
+        context,
+        refMode: refMode,
+        readTypeInfo: readTypeInfo
+    )
+    return wrapped?.map { $0.anyValueForCollection() }
+}
+
+public func writeStringAnyMap(
+    _ value: [String: Any]?,
+    context: WriteContext,
+    refMode: RefMode,
+    writeTypeInfo: Bool = false,
+    hasGenerics: Bool = true
+) throws {
+    let wrapped = value?.reduce(into: [String: DynamicAnyValue]()) { result, 
pair in
+        result[pair.key] = DynamicAnyValue.wrapped(pair.value)
+    }
+    try wrapped.foryWrite(
+        context,
+        refMode: refMode,
+        writeTypeInfo: writeTypeInfo,
+        hasGenerics: hasGenerics
+    )
+}
+
+public func readStringAnyMap(
+    context: ReadContext,
+    refMode: RefMode,
+    readTypeInfo: Bool = false
+) throws -> [String: Any]? {
+    let wrapped: [String: DynamicAnyValue]? = try [String: 
DynamicAnyValue]?.foryRead(
+        context,
+        refMode: refMode,
+        readTypeInfo: readTypeInfo
+    )
+    guard let wrapped else {
+        return nil
+    }
+    var map: [String: Any] = [:]
+    map.reserveCapacity(wrapped.count)
+    for pair in wrapped {
+        map[pair.key] = pair.value.anyValueForCollection()
+    }
+    return map
+}
+
+public func writeInt32AnyMap(
+    _ value: [Int32: Any]?,
+    context: WriteContext,
+    refMode: RefMode,
+    writeTypeInfo: Bool = false,
+    hasGenerics: Bool = true
+) throws {
+    let wrapped = value?.reduce(into: [Int32: DynamicAnyValue]()) { result, 
pair in
+        result[pair.key] = DynamicAnyValue.wrapped(pair.value)
+    }
+    try wrapped.foryWrite(
+        context,
+        refMode: refMode,
+        writeTypeInfo: writeTypeInfo,
+        hasGenerics: hasGenerics
+    )
+}
+
+public func readInt32AnyMap(
+    context: ReadContext,
+    refMode: RefMode,
+    readTypeInfo: Bool = false
+) throws -> [Int32: Any]? {
+    let wrapped: [Int32: DynamicAnyValue]? = try [Int32: 
DynamicAnyValue]?.foryRead(
+        context,
+        refMode: refMode,
+        readTypeInfo: readTypeInfo
+    )
+    guard let wrapped else {
+        return nil
+    }
+    var map: [Int32: Any] = [:]
+    map.reserveCapacity(wrapped.count)
+    for pair in wrapped {
+        map[pair.key] = pair.value.anyValueForCollection()
+    }
+    return map
+}
+
+func readDynamicAnyMapValue(context: ReadContext) throws -> Any {
+    let mapStart = context.reader.getCursor()
+    let keyTypeID = try peekDynamicMapKeyTypeID(context: context)
+    context.reader.setCursor(mapStart)
+
+    switch keyTypeID {
+    case .int32, .varint32:
+        return try readInt32AnyMap(context: context, refMode: .none) ?? [:]
+    case nil, .string:
+        return try readStringAnyMap(context: context, refMode: .none) ?? [:]
+    default:
+        throw ForyError.invalidData("unsupported dynamic map key type 
\(String(describing: keyTypeID))")
+    }
+}
+
+private func peekDynamicMapKeyTypeID(context: ReadContext) throws -> 
ForyTypeId? {
+    let start = context.reader.getCursor()
+    defer {
+        context.reader.setCursor(start)
+    }
+
+    let length = Int(try context.reader.readVarUInt32())
+    if length == 0 {
+        return nil
+    }
+
+    let header = try context.reader.readUInt8()
+    let keyNull = (header & DynamicAnyMapHeader.keyNull) != 0
+    let keyDeclared = (header & DynamicAnyMapHeader.declaredKeyType) != 0
+    let valueNull = (header & DynamicAnyMapHeader.valueNull) != 0
+
+    if keyDeclared {
+        return nil
+    }
+    if keyNull {
+        return nil
+    }
+
+    if !valueNull {
+        _ = try context.reader.readUInt8()
+    }
+
+    let rawTypeID = try context.reader.readVarUInt32()
+    return ForyTypeId(rawValue: rawTypeID)
+}
diff --git a/swift/Sources/ForySwift/Context.swift 
b/swift/Sources/ForySwift/Context.swift
index 8f09fb718..c7aefa8ad 100644
--- a/swift/Sources/ForySwift/Context.swift
+++ b/swift/Sources/ForySwift/Context.swift
@@ -371,3 +371,111 @@ public final class ReadContext {
         metaStringReadState.reset()
     }
 }
+
+public extension WriteContext {
+    func writeAny(
+        _ value: Any?,
+        refMode: RefMode,
+        writeTypeInfo: Bool = true,
+        hasGenerics: Bool = false
+    ) throws {
+        try ForySwift.writeAny(
+            value,
+            context: self,
+            refMode: refMode,
+            writeTypeInfo: writeTypeInfo,
+            hasGenerics: hasGenerics
+        )
+    }
+
+    func writeAnyList(
+        _ value: [Any]?,
+        refMode: RefMode,
+        writeTypeInfo: Bool = false,
+        hasGenerics: Bool = true
+    ) throws {
+        try ForySwift.writeAnyList(
+            value,
+            context: self,
+            refMode: refMode,
+            writeTypeInfo: writeTypeInfo,
+            hasGenerics: hasGenerics
+        )
+    }
+
+    func writeStringAnyMap(
+        _ value: [String: Any]?,
+        refMode: RefMode,
+        writeTypeInfo: Bool = false,
+        hasGenerics: Bool = true
+    ) throws {
+        try ForySwift.writeStringAnyMap(
+            value,
+            context: self,
+            refMode: refMode,
+            writeTypeInfo: writeTypeInfo,
+            hasGenerics: hasGenerics
+        )
+    }
+
+    func writeInt32AnyMap(
+        _ value: [Int32: Any]?,
+        refMode: RefMode,
+        writeTypeInfo: Bool = false,
+        hasGenerics: Bool = true
+    ) throws {
+        try ForySwift.writeInt32AnyMap(
+            value,
+            context: self,
+            refMode: refMode,
+            writeTypeInfo: writeTypeInfo,
+            hasGenerics: hasGenerics
+        )
+    }
+}
+
+public extension ReadContext {
+    func readAny(
+        refMode: RefMode,
+        readTypeInfo: Bool = true
+    ) throws -> Any? {
+        try ForySwift.readAny(
+            context: self,
+            refMode: refMode,
+            readTypeInfo: readTypeInfo
+        )
+    }
+
+    func readAnyList(
+        refMode: RefMode,
+        readTypeInfo: Bool = false
+    ) throws -> [Any]? {
+        try ForySwift.readAnyList(
+            context: self,
+            refMode: refMode,
+            readTypeInfo: readTypeInfo
+        )
+    }
+
+    func readStringAnyMap(
+        refMode: RefMode,
+        readTypeInfo: Bool = false
+    ) throws -> [String: Any]? {
+        try ForySwift.readStringAnyMap(
+            context: self,
+            refMode: refMode,
+            readTypeInfo: readTypeInfo
+        )
+    }
+
+    func readInt32AnyMap(
+        refMode: RefMode,
+        readTypeInfo: Bool = false
+    ) throws -> [Int32: Any]? {
+        try ForySwift.readInt32AnyMap(
+            context: self,
+            refMode: refMode,
+            readTypeInfo: readTypeInfo
+        )
+    }
+}
diff --git a/swift/Sources/ForySwift/Fory.swift 
b/swift/Sources/ForySwift/Fory.swift
index d0e97c685..d85c284f5 100644
--- a/swift/Sources/ForySwift/Fory.swift
+++ b/swift/Sources/ForySwift/Fory.swift
@@ -131,6 +131,423 @@ public final class Fory {
         return value
     }
 
+    @_disfavoredOverload
+    public func serialize(_ value: Any) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeAny(value, refMode: refMode, writeTypeInfo: true, 
hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: Any.Type = Any.self) throws -> 
Any {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return ForyAnyNullValue()
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: Any.self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serialize(_ value: AnyObject) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeAny(value, refMode: refMode, writeTypeInfo: true, 
hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: AnyObject.Type = 
AnyObject.self) throws -> AnyObject {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return NSNull()
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: AnyObject.self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serialize(_ value: any Serializer) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeAny(value, refMode: refMode, writeTypeInfo: true, 
hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: (any Serializer).Type = (any 
Serializer).self) throws -> any Serializer {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return ForyAnyNullValue()
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: (any Serializer).self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serialize(_ value: [Any]) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeAnyList(value, refMode: refMode, writeTypeInfo: true, 
hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: [Any].Type = [Any].self) 
throws -> [Any] {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return []
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readAnyList(refMode: refMode, readTypeInfo: 
true) ?? []
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serialize(_ value: [String: Any]) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeStringAnyMap(value, refMode: refMode, writeTypeInfo: 
true, hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: [String: Any].Type = [String: 
Any].self) throws -> [String: Any] {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return [:]
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readStringAnyMap(refMode: refMode, 
readTypeInfo: true) ?? [:]
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serialize(_ value: [Int32: Any]) throws -> Data {
+        let writer = ByteWriter()
+        writeHead(writer: writer, isNone: false)
+
+        let context = WriteContext(
+            writer: writer,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefWriteState(),
+            metaStringWriteState: MetaStringWriteState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        try context.writeInt32AnyMap(value, refMode: refMode, writeTypeInfo: 
true, hasGenerics: false)
+        context.resetObjectState()
+        return writer.toData()
+    }
+
+    @_disfavoredOverload
+    public func deserialize(_ data: Data, as _: [Int32: Any].Type = [Int32: 
Any].self) throws -> [Int32: Any] {
+        let reader = ByteReader(data: data)
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return [:]
+        }
+
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readInt32AnyMap(refMode: refMode, 
readTypeInfo: true) ?? [:]
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: [Any]) throws {
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: Any) throws {
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(_ reader: ByteReader, as _: Any.Type = 
Any.self) throws -> Any {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return ForyAnyNullValue()
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: Any.self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: AnyObject) throws {
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(_ reader: ByteReader, as _: AnyObject.Type = 
AnyObject.self) throws -> AnyObject {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return NSNull()
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: AnyObject.self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: any Serializer) 
throws {
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(
+        _ reader: ByteReader,
+        as _: (any Serializer).Type = (any Serializer).self
+    ) throws -> any Serializer {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return ForyAnyNullValue()
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try castAnyDynamicValue(
+            context.readAny(refMode: refMode, readTypeInfo: true),
+            to: (any Serializer).self
+        )
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(_ reader: ByteReader, as _: [Any].Type = 
[Any].self) throws -> [Any] {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return []
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readAnyList(refMode: refMode, readTypeInfo: 
true) ?? []
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: [String: Any]) throws 
{
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(_ reader: ByteReader, as _: [String: Any].Type 
= [String: Any].self) throws -> [String: Any] {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return [:]
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readStringAnyMap(refMode: refMode, 
readTypeInfo: true) ?? [:]
+        context.resetObjectState()
+        return value
+    }
+
+    @_disfavoredOverload
+    public func serializeTo(_ buffer: inout Data, value: [Int32: Any]) throws {
+        buffer.append(try serialize(value))
+    }
+
+    @_disfavoredOverload
+    public func deserializeFrom(_ reader: ByteReader, as _: [Int32: Any].Type 
= [Int32: Any].self) throws -> [Int32: Any] {
+        let isNone = try readHead(reader: reader)
+        if isNone {
+            return [:]
+        }
+        let context = ReadContext(
+            reader: reader,
+            typeResolver: typeResolver,
+            trackRef: config.trackRef,
+            compatible: config.compatible,
+            compatibleTypeDefState: CompatibleTypeDefReadState(),
+            metaStringReadState: MetaStringReadState()
+        )
+        let refMode: RefMode = config.trackRef ? .tracking : .nullOnly
+        let value = try context.readInt32AnyMap(refMode: refMode, 
readTypeInfo: true) ?? [:]
+        context.resetObjectState()
+        return value
+    }
+
     public func writeHead(writer: ByteWriter, isNone: Bool) {
         var bitmap: UInt8 = 0
         if config.xlang {
diff --git a/swift/Sources/ForySwift/RefResolver.swift 
b/swift/Sources/ForySwift/RefResolver.swift
index 58a96a39f..901706f48 100644
--- a/swift/Sources/ForySwift/RefResolver.swift
+++ b/swift/Sources/ForySwift/RefResolver.swift
@@ -74,6 +74,14 @@ public final class RefReader {
         return value
     }
 
+    public func readRefValue(_ refID: UInt32) throws -> Any {
+        let index = Int(refID)
+        guard refs.indices.contains(index) else {
+            throw ForyError.refError("ref_id out of range: \(refID)")
+        }
+        return refs[index]
+    }
+
     public func reset() {
         refs.removeAll(keepingCapacity: true)
     }
diff --git a/swift/Sources/ForySwift/TypeResolver.swift 
b/swift/Sources/ForySwift/TypeResolver.swift
index 31711b2cb..2e4a95fa9 100644
--- a/swift/Sources/ForySwift/TypeResolver.swift
+++ b/swift/Sources/ForySwift/TypeResolver.swift
@@ -44,6 +44,12 @@ private struct TypeNameKey: Hashable {
     let typeName: String
 }
 
+private enum DynamicRegistrationMode {
+    case idOnly
+    case nameOnly
+    case mixed
+}
+
 private struct TypeReader {
     let kind: ForyTypeId
     let reader: (ReadContext) throws -> Any
@@ -54,6 +60,7 @@ public final class TypeResolver {
     private var bySwiftType: [ObjectIdentifier: RegisteredTypeInfo] = [:]
     private var byUserTypeID: [UInt32: TypeReader] = [:]
     private var byTypeName: [TypeNameKey: TypeReader] = [:]
+    private var registrationModeByKind: [ForyTypeId: DynamicRegistrationMode] 
= [:]
 
     public init() {}
 
@@ -67,6 +74,7 @@ public final class TypeResolver {
             typeName: MetaString.empty(specialChar1: "$", specialChar2: "_")
         )
         bySwiftType[key] = info
+        markRegistrationMode(kind: info.kind, registerByName: false)
         byUserTypeID[id] = TypeReader(
             kind: T.staticTypeId,
             reader: { context in
@@ -97,6 +105,7 @@ public final class TypeResolver {
             typeName: typeNameMeta
         )
         bySwiftType[key] = info
+        markRegistrationMode(kind: info.kind, registerByName: true)
         byTypeName[TypeNameKey(namespace: namespace, typeName: typeName)] = 
TypeReader(
             kind: T.staticTypeId,
             reader: { context in
@@ -160,4 +169,241 @@ public final class TypeResolver {
         }
         return try entry.reader(context)
     }
+
+    public func readDynamicTypeInfo(context: ReadContext) throws -> 
DynamicTypeInfo {
+        let rawTypeID = try context.reader.readVarUInt32()
+        guard let wireTypeID = ForyTypeId(rawValue: rawTypeID) else {
+            throw ForyError.invalidData("unknown dynamic type id \(rawTypeID)")
+        }
+
+        switch wireTypeID {
+        case .compatibleStruct, .namedCompatibleStruct:
+            let typeMeta = try context.readCompatibleTypeMeta()
+            if typeMeta.registerByName {
+                return DynamicTypeInfo(
+                    wireTypeID: wireTypeID,
+                    userTypeID: nil,
+                    namespace: typeMeta.namespace,
+                    typeName: typeMeta.typeName,
+                    compatibleTypeMeta: typeMeta
+                )
+            }
+            return DynamicTypeInfo(
+                wireTypeID: wireTypeID,
+                userTypeID: typeMeta.userTypeID,
+                namespace: nil,
+                typeName: nil,
+                compatibleTypeMeta: typeMeta
+            )
+        case .namedStruct, .namedEnum, .namedExt, .namedUnion:
+            let namespace = try Self.readMetaString(
+                reader: context.reader,
+                decoder: .namespace,
+                encodings: namespaceMetaStringEncodings
+            )
+            let typeName = try Self.readMetaString(
+                reader: context.reader,
+                decoder: .typeName,
+                encodings: typeNameMetaStringEncodings
+            )
+            return DynamicTypeInfo(
+                wireTypeID: wireTypeID,
+                userTypeID: nil,
+                namespace: namespace,
+                typeName: typeName,
+                compatibleTypeMeta: nil
+            )
+        case .structType, .enumType, .ext, .typedUnion:
+            switch try dynamicRegistrationMode(for: wireTypeID) {
+            case .idOnly:
+                return DynamicTypeInfo(
+                    wireTypeID: wireTypeID,
+                    userTypeID: try context.reader.readVarUInt32(),
+                    namespace: nil,
+                    typeName: nil,
+                    compatibleTypeMeta: nil
+                )
+            case .nameOnly:
+                let namespace = try Self.readMetaString(
+                    reader: context.reader,
+                    decoder: .namespace,
+                    encodings: namespaceMetaStringEncodings
+                )
+                let typeName = try Self.readMetaString(
+                    reader: context.reader,
+                    decoder: .typeName,
+                    encodings: typeNameMetaStringEncodings
+                )
+                return DynamicTypeInfo(
+                    wireTypeID: wireTypeID,
+                    userTypeID: nil,
+                    namespace: namespace,
+                    typeName: typeName,
+                    compatibleTypeMeta: nil
+                )
+            case .mixed:
+                throw ForyError.invalidData("ambiguous dynamic type 
registration mode for \(wireTypeID)")
+            }
+        default:
+            return DynamicTypeInfo(
+                wireTypeID: wireTypeID,
+                userTypeID: nil,
+                namespace: nil,
+                typeName: nil,
+                compatibleTypeMeta: nil
+            )
+        }
+    }
+
+    public func readDynamicValue(typeInfo: DynamicTypeInfo, context: 
ReadContext) throws -> Any {
+        let value: Any
+        switch typeInfo.wireTypeID {
+        case .bool:
+            value = try Bool.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int8:
+            value = try Int8.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int16:
+            value = try Int16.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int32:
+            value = try ForyInt32Fixed.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .varint32:
+            value = try Int32.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int64:
+            value = try ForyInt64Fixed.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .varint64:
+            value = try Int64.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .taggedInt64:
+            value = try ForyInt64Tagged.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .uint8:
+            value = try UInt8.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .uint16:
+            value = try UInt16.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .uint32:
+            value = try ForyUInt32Fixed.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .varUInt32:
+            value = try UInt32.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .uint64:
+            value = try ForyUInt64Fixed.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .varUInt64:
+            value = try UInt64.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .taggedUInt64:
+            value = try ForyUInt64Tagged.foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .float32:
+            value = try Float.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .float64:
+            value = try Double.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .string:
+            value = try String.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .binary, .uint8Array:
+            value = try Data.foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .boolArray:
+            value = try [Bool].foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int8Array:
+            value = try [Int8].foryRead(context, refMode: .none, readTypeInfo: 
false)
+        case .int16Array:
+            value = try [Int16].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .int32Array:
+            value = try [Int32].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .int64Array:
+            value = try [Int64].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .uint16Array:
+            value = try [UInt16].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .uint32Array:
+            value = try [UInt32].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .uint64Array:
+            value = try [UInt64].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .float32Array:
+            value = try [Float].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .float64Array:
+            value = try [Double].foryRead(context, refMode: .none, 
readTypeInfo: false)
+        case .list:
+            value = try context.readAnyList(refMode: .none) ?? []
+        case .map:
+            value = try readDynamicAnyMapValue(context: context)
+        case .structType, .enumType, .ext, .typedUnion:
+            if let userTypeID = typeInfo.userTypeID {
+                value = try readByUserTypeID(userTypeID, context: context)
+            } else if let namespace = typeInfo.namespace, let typeName = 
typeInfo.typeName {
+                value = try readByTypeName(
+                    namespace: namespace.value,
+                    typeName: typeName.value,
+                    context: context
+                )
+            } else {
+                throw ForyError.invalidData("missing dynamic registration info 
for \(typeInfo.wireTypeID)")
+            }
+        case .namedStruct, .namedEnum, .namedExt, .namedUnion:
+            guard let namespace = typeInfo.namespace, let typeName = 
typeInfo.typeName else {
+                throw ForyError.invalidData("missing dynamic type name for 
\(typeInfo.wireTypeID)")
+            }
+            value = try readByTypeName(
+                namespace: namespace.value,
+                typeName: typeName.value,
+                context: context
+            )
+        case .compatibleStruct, .namedCompatibleStruct:
+            guard let compatibleTypeMeta = typeInfo.compatibleTypeMeta else {
+                throw ForyError.invalidData("missing compatible type meta for 
\(typeInfo.wireTypeID)")
+            }
+            if compatibleTypeMeta.registerByName {
+                value = try readByTypeName(
+                    namespace: compatibleTypeMeta.namespace.value,
+                    typeName: compatibleTypeMeta.typeName.value,
+                    context: context,
+                    compatibleTypeMeta: compatibleTypeMeta
+                )
+            } else {
+                guard let userTypeID = compatibleTypeMeta.userTypeID else {
+                    throw ForyError.invalidData("missing user type id in 
compatible dynamic type meta")
+                }
+                value = try readByUserTypeID(
+                    userTypeID,
+                    context: context,
+                    compatibleTypeMeta: compatibleTypeMeta
+                )
+            }
+        case .none:
+            value = ForyAnyNullValue()
+        default:
+            throw ForyError.invalidData("unsupported dynamic type id 
\(typeInfo.wireTypeID)")
+        }
+        return value
+    }
+
+    private func markRegistrationMode(kind: ForyTypeId, registerByName: Bool) {
+        let mode: DynamicRegistrationMode = registerByName ? .nameOnly : 
.idOnly
+        guard let existing = registrationModeByKind[kind] else {
+            registrationModeByKind[kind] = mode
+            return
+        }
+        if existing != mode {
+            registrationModeByKind[kind] = .mixed
+        }
+    }
+
+    private func dynamicRegistrationMode(for kind: ForyTypeId) throws -> 
DynamicRegistrationMode {
+        guard let mode = registrationModeByKind[kind] else {
+            throw ForyError.typeNotRegistered("no dynamic registration mode 
for kind \(kind)")
+        }
+        return mode
+    }
+
+    private static func readMetaString(
+        reader: ByteReader,
+        decoder: MetaStringDecoder,
+        encodings: [MetaStringEncoding]
+    ) throws -> MetaString {
+        let header = try reader.readUInt8()
+        let encodingIndex = Int(header & 0b11)
+        guard encodingIndex < encodings.count else {
+            throw ForyError.invalidData("invalid meta string encoding index")
+        }
+
+        var length = Int(header >> 2)
+        if length >= 0b11_1111 {
+            length = 0b11_1111 + Int(try reader.readVarUInt32())
+        }
+        let bytes = try reader.readBytes(count: length)
+        return try decoder.decode(bytes: bytes, encoding: 
encodings[encodingIndex])
+    }
 }
diff --git a/swift/Sources/ForySwiftMacros/ForyObjectMacro.swift 
b/swift/Sources/ForySwiftMacros/ForyObjectMacro.swift
index bb475efa9..b0a5bc5f3 100644
--- a/swift/Sources/ForySwiftMacros/ForyObjectMacro.swift
+++ b/swift/Sources/ForySwiftMacros/ForyObjectMacro.swift
@@ -100,6 +100,13 @@ private enum FieldEncoding: String {
     case tagged
 }
 
+private enum DynamicAnyCodecKind {
+    case anyValue
+    case anyList
+    case stringAnyMap
+    case int32AnyMap
+}
+
 private struct ParsedField {
     let name: String
     let typeText: String
@@ -114,6 +121,7 @@ private struct ParsedField {
     let isCompressedNumeric: Bool
     let primitiveSize: Int
     let customCodecType: String?
+    let dynamicAnyCodec: DynamicAnyCodecKind?
 }
 
 private struct ParsedDecl {
@@ -170,6 +178,7 @@ private func parseFields(_ declaration: some 
DeclGroupSyntax) throws -> ParsedDe
                 concreteType: concreteType,
                 fieldEncoding: fieldEncoding
             )
+            let dynamicAnyCodec = try resolveDynamicAnyCodec(rawType: rawType)
             let classification = typeResolution.classification
             let group: Int
             if classification.isPrimitive {
@@ -196,7 +205,8 @@ private func parseFields(_ declaration: some 
DeclGroupSyntax) throws -> ParsedDe
                     typeID: classification.typeID,
                     isCompressedNumeric: classification.isCompressedNumeric,
                     primitiveSize: classification.primitiveSize,
-                    customCodecType: typeResolution.customCodecType
+                    customCodecType: typeResolution.customCodecType,
+                    dynamicAnyCodec: dynamicAnyCodec
                 )
             )
             originalIndex += 1
@@ -383,6 +393,62 @@ private func resolveFieldType(
     }
 }
 
+private func resolveDynamicAnyCodec(rawType: String) throws -> 
DynamicAnyCodecKind? {
+    let optional = unwrapOptional(rawType)
+    let concreteType = trimType(optional.type)
+
+    if isDynamicAnyConcreteType(concreteType) {
+        return .anyValue
+    }
+
+    if let elementType = parseArrayElement(concreteType), 
containsDynamicAny(typeText: elementType) {
+        return .anyList
+    }
+
+    if let elementType = parseSetElement(concreteType), 
containsDynamicAny(typeText: elementType) {
+        throw MacroExpansionErrorMessage("Set<...> with Any elements is not 
supported by @ForyObject yet")
+    }
+
+    if let (keyType, valueType) = parseDictionary(concreteType),
+       containsDynamicAny(typeText: keyType) || containsDynamicAny(typeText: 
valueType) {
+        let normalizedKeyType = trimType(unwrapOptional(keyType).type)
+        if normalizedKeyType == "String" {
+            return .stringAnyMap
+        }
+        if normalizedKeyType == "Int32" {
+            return .int32AnyMap
+        }
+        throw MacroExpansionErrorMessage(
+            "Dictionary<\(keyType), ...> with Any values is only supported for 
String or Int32 keys"
+        )
+    }
+
+    return nil
+}
+
+private func containsDynamicAny(typeText: String) -> Bool {
+    let optional = unwrapOptional(typeText)
+    let concreteType = trimType(optional.type)
+
+    if isDynamicAnyConcreteType(concreteType) {
+        return true
+    }
+
+    if let elementType = parseArrayElement(concreteType) {
+        return containsDynamicAny(typeText: elementType)
+    }
+
+    if let elementType = parseSetElement(concreteType) {
+        return containsDynamicAny(typeText: elementType)
+    }
+
+    if let (keyType, valueType) = parseDictionary(concreteType) {
+        return containsDynamicAny(typeText: keyType) || 
containsDynamicAny(typeText: valueType)
+    }
+
+    return false
+}
+
 private func sortFields(_ fields: [ParsedField]) -> [ParsedField] {
     fields.sorted { lhs, rhs in
         if lhs.group != rhs.group {
@@ -459,7 +525,17 @@ private func buildSchemaFingerprint(fields: [ParsedField]) 
-> String {
         .map { field -> String in
             let typeID = fingerprintTypeID(for: field)
             let nullable = field.isOptional ? "1" : "0"
-            let trackRefExpr = "(trackRef && 
\(field.typeText).isReferenceTrackableType) ? 1 : 0"
+            let trackRefExpr: String
+            if let dynamicAnyCodec = field.dynamicAnyCodec {
+                switch dynamicAnyCodec {
+                case .anyValue:
+                    trackRefExpr = "trackRef ? 1 : 0"
+                case .anyList, .stringAnyMap, .int32AnyMap:
+                    trackRefExpr = "0"
+                }
+            } else {
+                trackRefExpr = "(trackRef && 
\(field.typeText).isReferenceTrackableType) ? 1 : 0"
+            }
             return 
"\"\(field.fieldIdentifier),\(typeID),\\(\(trackRefExpr)),\(nullable);\""
         }
     if entries.isEmpty {
@@ -496,7 +572,12 @@ private func buildDefaultDecl(isClass: Bool, fields: 
[ParsedField]) -> String {
 
     let args = fields
         .sorted(by: { $0.originalIndex < $1.originalIndex })
-        .map { "\($0.name): \($0.typeText).foryDefault()" }
+        .map { field in
+            if field.dynamicAnyCodec != nil {
+                return "\(field.name): \(dynamicAnyDefaultExpr(typeText: 
field.typeText))"
+            }
+            return "\(field.name): \(field.typeText).foryDefault()"
+        }
         .joined(separator: ",\n            ")
 
     return """
@@ -538,6 +619,13 @@ private func buildWriteDataDecl(sortedFields: 
[ParsedField]) -> String {
 
 private func schemaWriteLine(for field: ParsedField) -> String {
     let refMode = fieldRefModeExpression(field)
+    if let dynamicAnyCodec = field.dynamicAnyCodec {
+        return dynamicAnyWriteLine(
+            field: field,
+            dynamicAnyCodec: dynamicAnyCodec,
+            refModeExpr: refMode
+        )
+    }
     let hasGenerics = field.isCollection ? "true" : "false"
     if let codecType = field.customCodecType {
         if field.isOptional {
@@ -550,6 +638,13 @@ private func schemaWriteLine(for field: ParsedField) -> 
String {
 
 private func compatibleWriteLine(for field: ParsedField) -> String {
     let refMode = fieldRefModeExpression(field)
+    if let dynamicAnyCodec = field.dynamicAnyCodec {
+        return dynamicAnyWriteLine(
+            field: field,
+            dynamicAnyCodec: dynamicAnyCodec,
+            refModeExpr: refMode
+        )
+    }
     let hasGenerics = field.isCollection ? "true" : "false"
     if let codecType = field.customCodecType {
         if field.isOptional {
@@ -648,7 +743,12 @@ private func buildReadDataDecl(isClass: Bool, fields: 
[ParsedField], sortedField
         .joined(separator: ",\n            ")
     let compatibleDefaults = fields
         .sorted(by: { $0.originalIndex < $1.originalIndex })
-        .map { "var __\($0.name) = \($0.typeText).foryDefault()" }
+        .map { field in
+            if field.dynamicAnyCodec != nil {
+                return "var __\(field.name): \(field.typeText) = 
\(dynamicAnyDefaultExpr(typeText: field.typeText))"
+            }
+            return "var __\(field.name) = \(field.typeText).foryDefault()"
+        }
         .joined(separator: "\n                ")
     let compatibleCases = sortedFields.map { field -> String in
         let valueExpr = readFieldExpr(
@@ -693,6 +793,14 @@ private func readFieldExpr(
     refModeExpr: String,
     readTypeInfoExpr: String
 ) -> String {
+    if let dynamicAnyCodec = field.dynamicAnyCodec {
+        return dynamicAnyReadExpr(
+            field: field,
+            dynamicAnyCodec: dynamicAnyCodec,
+            refModeExpr: refModeExpr,
+            readTypeInfoExpr: readTypeInfoExpr
+        )
+    }
     if let codecType = field.customCodecType {
         if field.isOptional {
             return "try \(codecType)?.foryRead(context, refMode: 
\(refModeExpr), readTypeInfo: false)?.rawValue"
@@ -702,8 +810,61 @@ private func readFieldExpr(
     return "try \(field.typeText).foryRead(context, refMode: \(refModeExpr), 
readTypeInfo: \(readTypeInfoExpr))"
 }
 
+private func dynamicAnyWriteLine(
+    field: ParsedField,
+    dynamicAnyCodec: DynamicAnyCodecKind,
+    refModeExpr: String
+) -> String {
+    switch dynamicAnyCodec {
+    case .anyValue:
+        return "try context.writeAny(self.\(field.name), refMode: 
\(refModeExpr), writeTypeInfo: true, hasGenerics: false)"
+    case .anyList:
+        if field.isOptional {
+            return "try context.writeAnyList(self.\(field.name) as [Any]?, 
refMode: \(refModeExpr), hasGenerics: true)"
+        }
+        return "try context.writeAnyList(self.\(field.name) as [Any], refMode: 
\(refModeExpr), hasGenerics: true)"
+    case .stringAnyMap:
+        if field.isOptional {
+            return "try context.writeStringAnyMap(self.\(field.name) as 
[String: Any]?, refMode: \(refModeExpr), hasGenerics: true)"
+        }
+        return "try context.writeStringAnyMap(self.\(field.name) as [String: 
Any], refMode: \(refModeExpr), hasGenerics: true)"
+    case .int32AnyMap:
+        if field.isOptional {
+            return "try context.writeInt32AnyMap(self.\(field.name) as [Int32: 
Any]?, refMode: \(refModeExpr), hasGenerics: true)"
+        }
+        return "try context.writeInt32AnyMap(self.\(field.name) as [Int32: 
Any], refMode: \(refModeExpr), hasGenerics: true)"
+    }
+}
+
+private func dynamicAnyReadExpr(
+    field: ParsedField,
+    dynamicAnyCodec: DynamicAnyCodecKind,
+    refModeExpr: String,
+    readTypeInfoExpr _: String
+) -> String {
+    let metatypeExpr = "(\(field.typeText)).self"
+    switch dynamicAnyCodec {
+    case .anyValue:
+        return "try castAnyDynamicValue(context.readAny(refMode: 
\(refModeExpr), readTypeInfo: true), to: \(metatypeExpr))"
+    case .anyList:
+        return "try castAnyDynamicValue(context.readAnyList(refMode: 
\(refModeExpr)), to: \(metatypeExpr))"
+    case .stringAnyMap:
+        return "try castAnyDynamicValue(context.readStringAnyMap(refMode: 
\(refModeExpr)), to: \(metatypeExpr))"
+    case .int32AnyMap:
+        return "try castAnyDynamicValue(context.readInt32AnyMap(refMode: 
\(refModeExpr)), to: \(metatypeExpr))"
+    }
+}
+
 private func fieldRefModeExpression(_ field: ParsedField) -> String {
     let nullable = field.isOptional ? "true" : "false"
+    if let dynamicAnyCodec = field.dynamicAnyCodec {
+        switch dynamicAnyCodec {
+        case .anyValue:
+            return "RefMode.from(nullable: \(nullable), trackRef: 
context.trackRef)"
+        case .anyList, .stringAnyMap, .int32AnyMap:
+            return "RefMode.from(nullable: \(nullable), trackRef: false)"
+        }
+    }
     return "RefMode.from(nullable: \(nullable), trackRef: context.trackRef && 
\(field.typeText).isReferenceTrackableType)"
 }
 
@@ -711,10 +872,22 @@ private func compatibleTypeMetaFieldExpression(
     _ field: ParsedField,
     trackRefExpression: String
 ) -> String {
-    buildCompatibleTypeMetaFieldTypeExpression(
+    let fieldTrackRefExpression: String
+    if let dynamicAnyCodec = field.dynamicAnyCodec {
+        switch dynamicAnyCodec {
+        case .anyValue:
+            fieldTrackRefExpression = trackRefExpression
+        case .anyList, .stringAnyMap, .int32AnyMap:
+            fieldTrackRefExpression = "false"
+        }
+    } else {
+        fieldTrackRefExpression = "\(trackRefExpression) && 
\(field.typeText).isReferenceTrackableType"
+    }
+
+    return buildCompatibleTypeMetaFieldTypeExpression(
         typeText: field.typeText,
         nullableExpression: field.isOptional ? "true" : "false",
-        trackRefExpression: "\(trackRefExpression) && 
\(field.typeText).isReferenceTrackableType",
+        trackRefExpression: fieldTrackRefExpression,
         explicitTypeID: field.customCodecType == nil ? nil : field.typeID
     )
 }
@@ -790,6 +963,8 @@ TypeMetaFieldType(
     let typeIDExpr: String
     if let explicitTypeID {
         typeIDExpr = "\(explicitTypeID)"
+    } else if isDynamicAnyConcreteType(concreteType) {
+        typeIDExpr = "UInt32(ForyTypeId.unknown.rawValue)"
     } else {
         typeIDExpr = "UInt32(\(concreteType).staticTypeId.rawValue)"
     }
@@ -840,6 +1015,9 @@ private struct TypeClassification {
 
 private func classifyType(_ typeText: String) -> TypeClassification {
     let normalized = trimType(typeText)
+    if isDynamicAnyConcreteType(normalized) {
+        return .init(typeID: 0, isPrimitive: false, isBuiltIn: true, 
isCollection: false, isMap: false, isCompressedNumeric: false, primitiveSize: 0)
+    }
 
     switch normalized {
     case "Bool":
@@ -916,6 +1094,57 @@ private func parseArrayElement(_ type: String) -> String? 
{
     return nil
 }
 
+private func dynamicAnyDefaultExpr(typeText: String) -> String {
+    let optional = unwrapOptional(typeText)
+    if optional.isOptional {
+        return "nil"
+    }
+
+    let concreteType = normalizeTypeForDynamicAny(optional.type)
+    if concreteType == "AnyObject" {
+        return "NSNull()"
+    }
+    if concreteType == "Any" || isAnySerializerExistentialType(concreteType) {
+        return "ForyAnyNullValue()"
+    }
+    if parseArrayElement(concreteType) != nil {
+        return "[]"
+    }
+    if parseDictionary(concreteType) != nil {
+        return "[:]"
+    }
+    return "\(typeText)()"
+}
+
+private func isDynamicAnyConcreteType(_ typeText: String) -> Bool {
+    let normalized = normalizeTypeForDynamicAny(typeText)
+    if normalized == "Any" || normalized == "AnyObject" {
+        return true
+    }
+    return isAnySerializerExistentialType(normalized)
+}
+
+private func isAnySerializerExistentialType(_ normalizedType: String) -> Bool {
+    let normalized = normalizeTypeForDynamicAny(normalizedType)
+    guard normalized.hasPrefix("any") else {
+        return false
+    }
+
+    let protocolType = String(normalized.dropFirst(3))
+    if protocolType == "Serializer" {
+        return true
+    }
+    return protocolType.hasSuffix(".Serializer")
+}
+
+private func normalizeTypeForDynamicAny(_ typeText: String) -> String {
+    var normalized = trimType(typeText)
+    while normalized.hasPrefix("("), normalized.hasSuffix(")"), 
normalized.count > 1 {
+        normalized = String(normalized.dropFirst().dropLast())
+    }
+    return normalized
+}
+
 private func parseSetElement(_ type: String) -> String? {
     if type.hasPrefix("Set<") && type.hasSuffix(">") {
         let start = type.index(type.startIndex, offsetBy: "Set<".count)
diff --git a/swift/Sources/ForySwiftXlangPeer/main.swift 
b/swift/Sources/ForySwiftXlangPeer/main.swift
index c63316ccc..d29404f8a 100644
--- a/swift/Sources/ForySwiftXlangPeer/main.swift
+++ b/swift/Sources/ForySwiftXlangPeer/main.swift
@@ -339,234 +339,14 @@ private struct Cat {
     var lives: Int32 = 0
 }
 
-private enum AnyAnimal: Serializer {
-    case dog(Dog)
-    case cat(Cat)
-
-    static func foryDefault() -> AnyAnimal {
-        .dog(.foryDefault())
-    }
-
-    static var staticTypeId: ForyTypeId {
-        .unknown
-    }
-
-    func foryWriteData(_ context: WriteContext, hasGenerics: Bool) throws {
-        switch self {
-        case .dog(let value):
-            try value.foryWriteData(context, hasGenerics: hasGenerics)
-        case .cat(let value):
-            try value.foryWriteData(context, hasGenerics: hasGenerics)
-        }
-    }
-
-    static func foryReadData(_ context: ReadContext) throws -> AnyAnimal {
-        guard let typeInfo = context.dynamicTypeInfo(for: Self.self) else {
-            throw ForyError.invalidData("AnyAnimal requires pre-read type info 
during decode")
-        }
-        return try decode(context, typeInfo: typeInfo)
-    }
-
-    static func foryWriteTypeInfo(_ context: WriteContext) throws {
-        _ = context
-        throw ForyError.invalidData("AnyAnimal type info is dynamic")
-    }
-
-    func foryWriteTypeInfo(_ context: WriteContext) throws {
-        switch self {
-        case .dog:
-            try Dog.foryWriteTypeInfo(context)
-        case .cat:
-            try Cat.foryWriteTypeInfo(context)
-        }
-    }
-
-    static func foryReadTypeInfo(_ context: ReadContext) throws {
-        let typeInfo = try parseDynamicTypeInfo(context)
-        context.setDynamicTypeInfo(for: Self.self, typeInfo)
-    }
-
-    func foryWrite(
-        _ context: WriteContext,
-        refMode: RefMode,
-        writeTypeInfo: Bool,
-        hasGenerics: Bool
-    ) throws {
-        if refMode != .none {
-            context.writer.writeInt8(RefFlag.notNullValue.rawValue)
-        }
-        switch self {
-        case .dog(let value):
-            try value.foryWrite(
-                context,
-                refMode: .none,
-                writeTypeInfo: writeTypeInfo,
-                hasGenerics: hasGenerics
-            )
-        case .cat(let value):
-            try value.foryWrite(
-                context,
-                refMode: .none,
-                writeTypeInfo: writeTypeInfo,
-                hasGenerics: hasGenerics
-            )
-        }
-    }
-
-    static func foryRead(
-        _ context: ReadContext,
-        refMode: RefMode,
-        readTypeInfo: Bool
-    ) throws -> AnyAnimal {
-        if refMode != .none {
-            let rawFlag = try context.reader.readInt8()
-            guard let flag = RefFlag(rawValue: rawFlag) else {
-                throw ForyError.refError("invalid ref flag \(rawFlag)")
-            }
-            switch flag {
-            case .null:
-                return .foryDefault()
-            case .notNullValue, .refValue:
-                break
-            case .ref:
-                throw ForyError.refError("AnyAnimal does not support 
reference-only payloads")
-            }
-        }
-
-        let typeInfo: DynamicTypeInfo
-        if readTypeInfo {
-            typeInfo = try parseDynamicTypeInfo(context)
-        } else if let pendingTypeInfo = context.dynamicTypeInfo(for: 
Self.self) {
-            typeInfo = pendingTypeInfo
-        } else {
-            throw ForyError.invalidData("AnyAnimal requires type info")
-        }
-        return try decode(context, typeInfo: typeInfo)
-    }
-
-    private static func parseDynamicTypeInfo(_ context: ReadContext) throws -> 
DynamicTypeInfo {
-        let rawTypeID = try context.reader.readVarUInt32()
-        guard let typeID = ForyTypeId(rawValue: rawTypeID) else {
-            throw ForyError.invalidData("unknown dynamic animal type id 
\(rawTypeID)")
-        }
-        switch typeID {
-        case .structType:
-            let userTypeID = try context.reader.readVarUInt32()
-            return DynamicTypeInfo(
-                wireTypeID: .structType,
-                userTypeID: userTypeID,
-                namespace: nil,
-                typeName: nil,
-                compatibleTypeMeta: nil
-            )
-        case .compatibleStruct:
-            let typeMeta = try context.readCompatibleTypeMeta()
-            guard let userTypeID = typeMeta.userTypeID else {
-                throw ForyError.invalidData("missing user type id for dynamic 
compatible animal")
-            }
-            return DynamicTypeInfo(
-                wireTypeID: .compatibleStruct,
-                userTypeID: userTypeID,
-                namespace: nil,
-                typeName: nil,
-                compatibleTypeMeta: typeMeta
-            )
-        case .namedStruct:
-            let namespace = try readPeerMetaString(
-                context.reader,
-                decoder: .namespace,
-                encodings: namespaceMetaStringEncodings
-            )
-            let typeName = try readPeerMetaString(
-                context.reader,
-                decoder: .typeName,
-                encodings: typeNameMetaStringEncodings
-            )
-            return DynamicTypeInfo(
-                wireTypeID: .namedStruct,
-                userTypeID: nil,
-                namespace: namespace,
-                typeName: typeName,
-                compatibleTypeMeta: nil
-            )
-        case .namedCompatibleStruct:
-            let typeMeta = try context.readCompatibleTypeMeta()
-            return DynamicTypeInfo(
-                wireTypeID: .namedCompatibleStruct,
-                userTypeID: nil,
-                namespace: typeMeta.namespace,
-                typeName: typeMeta.typeName,
-                compatibleTypeMeta: typeMeta
-            )
-        default:
-            throw ForyError.invalidData("unsupported dynamic animal wire type 
\(typeID)")
-        }
-    }
-
-    private static func decode(_ context: ReadContext, typeInfo: 
DynamicTypeInfo) throws -> AnyAnimal {
-        let value: Any
-        switch typeInfo.wireTypeID {
-        case .structType:
-            guard let userTypeID = typeInfo.userTypeID else {
-                throw ForyError.invalidData("missing user type id for dynamic 
animal")
-            }
-            value = try context.typeResolver.readByUserTypeID(userTypeID, 
context: context)
-        case .compatibleStruct:
-            guard let userTypeID = typeInfo.userTypeID else {
-                throw ForyError.invalidData("missing user type id for dynamic 
compatible animal")
-            }
-            guard let compatibleTypeMeta = typeInfo.compatibleTypeMeta else {
-                throw ForyError.invalidData("missing compatible type meta for 
dynamic animal")
-            }
-            value = try context.typeResolver.readByUserTypeID(
-                userTypeID,
-                context: context,
-                compatibleTypeMeta: compatibleTypeMeta
-            )
-        case .namedStruct:
-            guard let namespace = typeInfo.namespace, let typeName = 
typeInfo.typeName else {
-                throw ForyError.invalidData("missing dynamic type name for 
animal")
-            }
-            value = try context.typeResolver.readByTypeName(
-                namespace: namespace.value,
-                typeName: typeName.value,
-                context: context
-            )
-        case .namedCompatibleStruct:
-            guard let namespace = typeInfo.namespace, let typeName = 
typeInfo.typeName else {
-                throw ForyError.invalidData("missing dynamic compatible type 
name for animal")
-            }
-            guard let compatibleTypeMeta = typeInfo.compatibleTypeMeta else {
-                throw ForyError.invalidData("missing compatible type meta for 
dynamic named animal")
-            }
-            value = try context.typeResolver.readByTypeName(
-                namespace: namespace.value,
-                typeName: typeName.value,
-                context: context,
-                compatibleTypeMeta: compatibleTypeMeta
-            )
-        default:
-            throw ForyError.invalidData("unsupported dynamic animal wire type 
\(typeInfo.wireTypeID)")
-        }
-
-        if let dog = value as? Dog {
-            return .dog(dog)
-        }
-        if let cat = value as? Cat {
-            return .cat(cat)
-        }
-        throw ForyError.invalidData("unsupported dynamic animal payload type")
-    }
-}
-
 @ForyObject
 private struct AnimalListHolder {
-    var animals: [AnyAnimal] = []
+    var animals: [Any] = []
 }
 
 @ForyObject
 private struct AnimalMapHolder {
-    var animalMap: [String: AnyAnimal] = [:]
+    var animalMap: [String: Any] = [:]
 }
 
 private enum StringOrLong: Serializer, Equatable {
@@ -711,26 +491,6 @@ private func debugLog(_ message: String) {
     }
 }
 
-private func readPeerMetaString(
-    _ reader: ByteReader,
-    decoder: MetaStringDecoder,
-    encodings: [MetaStringEncoding]
-) throws -> MetaString {
-    let header = try reader.readUInt8()
-    let encodingIndex = Int(header & 0b11)
-    guard encodingIndex < encodings.count else {
-        throw ForyError.invalidData("invalid meta string encoding index")
-    }
-
-    var length = Int(header >> 2)
-    let bigNameThreshold = 0b11_1111
-    if length >= bigNameThreshold {
-        length = bigNameThreshold + Int(try reader.readVarUInt32())
-    }
-    let bytes = try reader.readBytes(count: length)
-    return try decoder.decode(bytes: bytes, encoding: encodings[encodingIndex])
-}
-
 private func verifyBufferCase(_ caseName: String, _ payload: [UInt8]) throws 
-> [UInt8] {
     let reader = ByteReader(bytes: payload)
     let writer = ByteWriter(capacity: payload.count)
@@ -1050,7 +810,7 @@ private func handlePolymorphicList(_ bytes: [UInt8]) 
throws -> [UInt8] {
     let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: 
true))
     registerPolymorphicTypes(fory)
     return try roundTripStream(bytes) { reader, out in
-        let animals: [AnyAnimal] = try fory.deserializeFrom(reader)
+        let animals: [Any] = try fory.deserializeFrom(reader)
         let holder: AnimalListHolder = try fory.deserializeFrom(reader)
         try fory.serializeTo(&out, value: animals)
         try fory.serializeTo(&out, value: holder)
@@ -1061,7 +821,7 @@ private func handlePolymorphicMap(_ bytes: [UInt8]) throws 
-> [UInt8] {
     let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: 
true))
     registerPolymorphicTypes(fory)
     return try roundTripStream(bytes) { reader, out in
-        let animalMap: [String: AnyAnimal] = try fory.deserializeFrom(reader)
+        let animalMap: [String: Any] = try fory.deserializeFrom(reader)
         let holder: AnimalMapHolder = try fory.deserializeFrom(reader)
         try fory.serializeTo(&out, value: animalMap)
         try fory.serializeTo(&out, value: holder)
diff --git a/swift/Tests/ForySwiftTests/ForySwiftTests.swift 
b/swift/Tests/ForySwiftTests/ForySwiftTests.swift
index 78fe17d4a..7f85a7233 100644
--- a/swift/Tests/ForySwiftTests/ForySwiftTests.swift
+++ b/swift/Tests/ForySwiftTests/ForySwiftTests.swift
@@ -66,6 +66,29 @@ final class Node {
     }
 }
 
+@ForyObject
+struct AnyObjectHolder {
+    var value: AnyObject
+    var optionalValue: AnyObject?
+    var items: [AnyObject]
+}
+
+@ForyObject
+struct AnySerializerHolder {
+    var value: any Serializer
+    var items: [any Serializer]
+    var map: [String: any Serializer]
+}
+
+@ForyObject
+struct AnyFieldHolder {
+    var value: Any
+    var optionalValue: Any?
+    var list: [Any]
+    var stringMap: [String: Any]
+    var int32Map: [Int32: Any]
+}
+
 @Test
 func primitiveRoundTrip() throws {
     let fory = Fory()
@@ -209,6 +232,148 @@ func macroClassReferenceTracking() throws {
     #expect(decoded.next === decoded)
 }
 
+@Test
+func topLevelAnyRoundTrip() throws {
+    let fory = Fory()
+    fory.register(Address.self, id: 209)
+
+    let value: Any = Address(street: "AnyTop", zip: 8080)
+    let data = try fory.serialize(value)
+    let decoded: Any = try fory.deserialize(data)
+    #expect(decoded as? Address == Address(street: "AnyTop", zip: 8080))
+
+    var buffer = Data()
+    try fory.serializeTo(&buffer, value: value)
+    let decodedFrom: Any = try fory.deserializeFrom(ByteReader(data: buffer))
+    #expect(decodedFrom as? Address == Address(street: "AnyTop", zip: 8080))
+
+    let nullAny: Any = Optional<Int32>.none as Any
+    let nullData = try fory.serialize(nullAny)
+    let nullDecoded: Any = try fory.deserialize(nullData)
+    #expect(nullDecoded is ForyAnyNullValue)
+}
+
+@Test
+func topLevelAnyObjectRoundTrip() throws {
+    let fory = Fory(config: .init(xlang: true, trackRef: true))
+    fory.register(Node.self, id: 210)
+
+    let value: AnyObject = Node(value: 123)
+    let data = try fory.serialize(value)
+    let decoded: AnyObject = try fory.deserialize(data)
+
+    let node = decoded as? Node
+    #expect(node != nil)
+    #expect(node?.value == 123)
+
+    var buffer = Data()
+    try fory.serializeTo(&buffer, value: value)
+    let decodedFrom: AnyObject = try fory.deserializeFrom(ByteReader(data: 
buffer))
+    #expect((decodedFrom as? Node)?.value == 123)
+}
+
+@Test
+func topLevelAnySerializerRoundTrip() throws {
+    let fory = Fory()
+    fory.register(Address.self, id: 211)
+
+    let value: any Serializer = Address(street: "AnyStreet", zip: 9090)
+    let data = try fory.serialize(value)
+    let decoded: any Serializer = try fory.deserialize(data)
+
+    let address = decoded as? Address
+    #expect(address == Address(street: "AnyStreet", zip: 9090))
+
+    var buffer = Data()
+    try fory.serializeTo(&buffer, value: value)
+    let decodedFrom: any Serializer = try 
fory.deserializeFrom(ByteReader(data: buffer))
+    #expect(decodedFrom as? Address == Address(street: "AnyStreet", zip: 9090))
+}
+
+@Test
+func macroDynamicAnyObjectAndAnySerializerFieldsRoundTrip() throws {
+    let fory = Fory(config: .init(xlang: true, trackRef: true))
+    fory.register(Node.self, id: 220)
+    fory.register(Address.self, id: 221)
+    fory.register(AnyObjectHolder.self, id: 222)
+    fory.register(AnySerializerHolder.self, id: 223)
+
+    let sharedNode = Node(value: 77)
+    let objectHolder = AnyObjectHolder(
+        value: sharedNode,
+        optionalValue: nil,
+        items: [sharedNode, NSNull()]
+    )
+    let objectData = try fory.serialize(objectHolder)
+    let objectDecoded: AnyObjectHolder = try fory.deserialize(objectData)
+    #expect((objectDecoded.value as? Node)?.value == 77)
+    #expect(objectDecoded.optionalValue == nil)
+    #expect(objectDecoded.items.count == 2)
+    #expect((objectDecoded.items[0] as? Node)?.value == 77)
+    #expect(objectDecoded.items[1] is NSNull)
+
+    let serializerHolder = AnySerializerHolder(
+        value: Address(street: "Root", zip: 10001),
+        items: [Int32(11), Address(street: "Nested", zip: 10002)],
+        map: [
+            "age": Int64(19),
+            "address": Address(street: "Mapped", zip: 10003),
+        ]
+    )
+    let serializerData = try fory.serialize(serializerHolder)
+    let serializerDecoded: AnySerializerHolder = try 
fory.deserialize(serializerData)
+
+    #expect(serializerDecoded.value as? Address == Address(street: "Root", 
zip: 10001))
+    #expect(serializerDecoded.items.count == 2)
+    #expect(serializerDecoded.items[0] as? Int32 == 11)
+    #expect(serializerDecoded.items[1] as? Address == Address(street: 
"Nested", zip: 10002))
+    #expect(serializerDecoded.map["age"] as? Int64 == 19)
+    #expect(serializerDecoded.map["address"] as? Address == Address(street: 
"Mapped", zip: 10003))
+}
+
+@Test
+func macroAnyFieldsRoundTrip() throws {
+    let fory = Fory()
+    fory.register(Address.self, id: 224)
+    fory.register(AnyFieldHolder.self, id: 225)
+
+    let value = AnyFieldHolder(
+        value: Address(street: "AnyRoot", zip: 11001),
+        optionalValue: nil,
+        list: [Int32(7), "hello", Address(street: "AnyList", zip: 11002), 
NSNull()],
+        stringMap: [
+            "count": Int64(3),
+            "name": "map",
+            "address": Address(street: "AnyMap", zip: 11003),
+            "empty": NSNull(),
+        ],
+        int32Map: [
+            1: Int32(-9),
+            2: "v2",
+            3: Address(street: "AnyIntMap", zip: 11004),
+            4: NSNull(),
+        ]
+    )
+    let data = try fory.serialize(value)
+    let decoded: AnyFieldHolder = try fory.deserialize(data)
+
+    #expect(decoded.value as? Address == Address(street: "AnyRoot", zip: 
11001))
+    #expect(decoded.optionalValue == nil)
+    #expect(decoded.list.count == 4)
+    #expect(decoded.list[0] as? Int32 == 7)
+    #expect(decoded.list[1] as? String == "hello")
+    #expect(decoded.list[2] as? Address == Address(street: "AnyList", zip: 
11002))
+    #expect(decoded.list[3] is NSNull)
+    #expect(decoded.stringMap["count"] as? Int64 == 3)
+    #expect(decoded.stringMap["name"] as? String == "map")
+    #expect(decoded.stringMap["address"] as? Address == Address(street: 
"AnyMap", zip: 11003))
+    #expect(decoded.stringMap["empty"] is NSNull)
+    #expect(decoded.int32Map[1] as? Int32 == -9)
+    #expect(decoded.int32Map[2] as? String == "v2")
+    #expect(decoded.int32Map[3] as? Address == Address(street: "AnyIntMap", 
zip: 11004))
+    #expect(decoded.int32Map[4] is NSNull)
+}
+
 @Test
 func collectionAndMapReferenceTracking() throws {
     let fory = Fory(config: .init(xlang: true, trackRef: true))


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to