{"id":15038960,"url":"https://github.com/brightify/datamapper","last_synced_at":"2025-10-29T02:45:00.899Z","repository":{"id":56908229,"uuid":"79444118","full_name":"Brightify/DataMapper","owner":"Brightify","description":"Universal object deserialization/serialization in Swift.","archived":false,"fork":false,"pushed_at":"2020-11-11T15:07:37.000Z","size":194,"stargazers_count":6,"open_issues_count":3,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-10T00:02:29.342Z","etag":null,"topics":["deserialization","framework","json","serialization","swift-4"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Brightify.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-19T10:50:41.000Z","updated_at":"2023-05-19T12:35:01.000Z","dependencies_parsed_at":"2022-08-20T19:50:23.358Z","dependency_job_id":null,"html_url":"https://github.com/Brightify/DataMapper","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Brightify%2FDataMapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Brightify%2FDataMapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Brightify%2FDataMapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Brightify%2FDataMapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Brightify","download_url":"https://codeload.github.com/Brightify/DataMapper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131321,"owners_count":21052819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deserialization","framework","json","serialization","swift-4"],"created_at":"2024-09-24T20:40:57.827Z","updated_at":"2025-10-29T02:45:00.829Z","avatar_url":"https://github.com/Brightify.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DataMapper\n\n[![CI Status](http://img.shields.io/travis/Brightify/DataMapper.svg?style=flat)](https://travis-ci.org/Brightify/DataMapper)\n[![Version](https://img.shields.io/cocoapods/v/DataMapper.svg?style=flat)](http://cocoapods.org/pods/DataMapper)\n[![License](https://img.shields.io/cocoapods/l/DataMapper.svg?style=flat)](http://cocoapods.org/pods/DataMapper)\n[![Platform](https://img.shields.io/cocoapods/p/DataMapper.svg?style=flat)](http://cocoapods.org/pods/DataMapper)\n[![Slack Status](http://swiftkit.brightify.org//badge.svg)](http://swiftkit.brightify.org)\n\n## Introduction\n\nDataMapper is a framework for safe deserialization/serialization of objects from/to different data representation standards (as of now we support JSON but others can be added easily).\n\nAmong its advantages belongs:\n\n* Easy to use API\n* Compile time safety (as much as possible)\n* Support for custom Serializers (allows you to simply change data representation format by implementing one class)\n* Polymorph\n* Thread safety (depends on your usage)\n* Support for one direction use (if you don't need the other one, you don't have to implement it)\n\n## Changelog\n\nList of all changes and new features can be found [here](CHANGELOG.md).\n\n## Requirements\n\n- **Swift 4**\n- **iOS 8+**\n\n## Installation\n\n### CocoaPods\n\nDataMapper is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your test target in your Podfile:\n\n```Ruby\npod \"DataMapper\"\n```\n\nThis will automatically include every subspec (Core and all Serializers).\n\nIf you want DataMapper without serializers (you have your own implementation), use:\n\n```Ruby\npod \"DataMapper/Core\"\n```\n\nEach implementation of serializer has its own subspec. For example:\n\n```Ruby\npod \"DataMapper/JsonSerializer\"\n```\n\nThese subspecs have Core as dependency, so there is no need to specify it explicitly.\n\n## Usage\n\nBelow is complete list of all features this library offers and how to use them. Some examples of usage can be found in [tests](Tests).\n\nUsed terminology:\n\n* mapping - either deserialization or serialization\n* Map protocol - `Deserializable`, `Serializable` or `Mappable`\n\n### Quick overview\n\n```Swift\n\t/*\n\t\t[{\n\t\t\t\"number\": 1,\n\t\t\t\"text\": \"A\"\n\t\t}, {\n\t\t\t\"number\": 2,\n\t\t\t\"text\": \"B\"\n\t\t}]\n\t*/\n\tlet inputData: NSData = ... // Some data in JSON to be deserialized.\n\n\tlet objectMapper = ObjectMapper()\n\tlet serializer = JsonSerializer()\n\n\t// Deserialization\n\tlet type = serializer.deserialize(inputData)\n\tlet objects: [MyObject]? = objectMapper.deserialize(type)\n\n\t... // Do some stuff with objects.\n\n\t// Serialization\n\tlet changedType = objectMapper.serialize(objects)\n\tlet outputData = serializer.serialize(changedType)\n\n\t// Can be deserilized and serialized.\n\tstruct MyObject: Mappable {\n\n\t\tvar number: Int?\n\t\tvar text: String?\n\n\t\tinit(_ data: DeserializableData) throws {\n\t\t\ttry mapping(data)\n\t\t}\n\n\t\tmutating func mapping(_ data: inout MappableData) throws {\n\t\t\tdata[\"number\"].map(\u0026number)\n\t\t\tdata[\"text\"].map(\u0026text)\n\t\t}\n\t}\n\n\t// Can be only deserialized.\n\tstruct MyDeserializableObject: Deserializable {\n\n\t\tlet number: Int?\n\t\tlet text: String?\n\n\t\tinit(_ data: DeserializableData) throws {\n\t\t\tnumber = data[\"number\"].get()\n\t\t\ttext = data[\"text\"].get()\n\t\t}\n\t}\n\n\t// Can be only serialized.\n\tstruct MySerializableObject: Serializable {\n\n\t\tlet number: Int?\n\t\tlet text: String?\n\n\t\tinit(number: Int?, text: String?) {\n\t\t\tself.number = number\n\t\t\tself.text = text\n\t\t}\n\n\t\tfunc serialize(to data: inout SerializableData) {\n\t\t\tdata[\"number\"].set(number)\n\t\t\tdata[\"text\"].set(text)\n\t\t}\n\t}\n```\n\n### SupportedType\n\n`SupportedType` creates intermediate level between ObjectMapper and Serializers. It is an enum like structure (for performance reasons implemented using class) representing basic data types (`null`, `string`, `bool`, `int`, `double`, `array`, `dictionary`). Each type has associated property which return its value if it is correct type or nil otherwise. With small exception for `null` whose property is named `isNull` and returns `Bool`.\n\n```Swift\nextension SupportedType {\n    \n    var isNull: Bool \n    \n    var string: String?\n    \n    var bool: Bool?\n    \n    var int: Int?\n    \n    var double: Double?\n    \n    var array: [SupportedType]?\n    \n    var dictionary: [String: SupportedType]? \n\n    mutating func addToDictionary(key: String, value: SupportedType)\n}\n```\n\nFor example:\n\n```Swift\nlet type: SupportedType = .string(\"A\")\ntype.string // \"A\"\ntype.number // nil\n```\n\n`addToDictionary` adds the key value pair to the dictionary. If the current type is not a dictionary, then it is replaced with a new dictionary. \n\n`SupportedType` can be created using these static methods:\n\n```Swift\nextension SupportedType {\n    \n    static var null: SupportedType\n\n    static func string(_ value: String) -\u003e SupportedType\n    \n    static func bool(_ value: Bool) -\u003e SupportedType\n    \n    static func int(_ value: Int) -\u003e SupportedType\n    \n    static func double(_ value: Double) -\u003e SupportedType \n    \n    static func array(_ value: [SupportedType]) -\u003e SupportedType\n    \n    static func dictionary(_ value: [String: SupportedType]) -\u003e SupportedType\n    \n    static func intOrDouble(_ value: Int) -\u003e SupportedType \n}\n```\n\n`intOrDouble` handles the problem of numbers being ambiguous when represented as text. For example: `1` is `Int` or `Double`. If `intOrDouble(1)` is used to create `SupportedType`, then both `.int` and `.double` returns `1`. On the contrary if you use `.int(1)`, only `.int` returns `1` and `.double` returns `nil`.\n\n### ObjectMapper\n\n```Swift\nfinal class ObjectMapper {\n\n\tfunc serialize\u003cT: Serializable\u003e(_ value: T?) -\u003e SupportedType\n\n\tfunc deserialize\u003cT: Deserializable\u003e(_ type: SupportedType) -\u003e T?\n\n\t// + other overloads for supported types mentioned below\n}\n```\n\n`ObjectMapper` maps objects to `SupportedType`. It has two types of methods: \n\n* `serialize` - Takes Swift objects and transforms them to `SupportedType`.\n* `deserialize` - Takes `SupportedType` and transforms them to Swift objects.\n\nSupported Swift types:\n\n* `T?`\n* `[T]?`\n* `[String: T]?`\n* `[T?]?`\n* `[String: T?]?`\n\nwhere `T` conforms to the Map protocol (depends on the method). If `T` does not conform to this protocol, you need to pass the instance of `Transformation` as the second parameter named `using`.\n\nAs you can see `deserialize` always returns an optional type. `nil` is returned if `SupportedType` is `.null` or cannot be converted to `T`.\n\n`serialize` accepts both optional and non optional types. If `nil` is passed then the result `SupportedType` is `.null`.\n\n`[T]?` differs from `[T?]?` in deserialization. If one of the elements from array is `nil` (`SupportedType` is `.null` or the object cannot be deserialized) then everything is discarded and `nil` is returned. In case of `[T?]?` `nil` value will be added to the array. The same applies to the dictionary.\n\n### Serializer\n\n```Swift\nprotocol Serializer {\n    \n    func serialize(_ supportedType: SupportedType) -\u003e Data\n    \n    func deserialize(_ data: Data) -\u003e SupportedType\n}\n```\n\n`Serializer` represents some object which maps `SupportedType` to `NSData`. You don't have to implement `Serializer` in order to map `SupportedType` to `NSData`, but it is recommended because then the object can be used in other libraries. (This protocol only provides standardized API.)\n\nSometimes (almost always) it is easier to work with `String` instead of `Data`. So we added extension methods to `Serializer`:\n\n```Swift\nextension Serializer {\n    \n    func serialize(toString supportedType: SupportedType) -\u003e String\n    \n    func deserialize(fromString string: String) -\u003e SupportedType\n}\n```\n\nNote: `String` is converted to `Data` (and back) using UTF-8 coding.\n\n#### TypedSerializer\n\n```Swift\nprotocol TypedSerializer: Serializer {\n    \n    associatedtype DataType\n    \n    func typedSerialize(_ supportedType: SupportedType) -\u003e DataType\n    \n    func typedDeserialize(_ data: DataType) -\u003e SupportedType\n}\n```\n\nExtends `Serializer` with generic type and methods. Sometimes you may get data from another library not as `NSData` but, for example, as JSON (`Any` with specific structure) and transforming them back and forth is not good for performance.\n\n#### Pre-implemented Serializers\n\n**JsonSerializer**\n\nAs its name suggests it works with JSON. It conforms to `TypedSerializer` protocol and the `DataType` is `Any`. Requirements for the data format (`Any` or `NSData`) are the same as in `NSJSONSerialization`.\n\n### Map protocol\n\n#### Deserializable\n\n```Swift\nprotocol Deserializable {\n\n    init(_ data: DeserializableData) throws\n}\n```\n\nAllows object conforming to this protocol to be deserialized from `SupportedType` using `ObjectMapper`. In this `init` you need to initialize the object using `DeserializableData` (see [DeserializableData](#deserializabledata)). If for some reason the object cannot be created (wrong data), then throw `DeserializationError`.\n\n#### Serializable\n\n```Swift\nprotocol Serializable {\n\n    func serialize(to data: inout SerializableData)\n}\n```\n\nAllows object conforming to this protocol to be serialized to `SupportedType` using `ObjectMapper`. In `serialize` set data you want to serialize (it does not have to be everything) to `SerializableData` (see [SerializableData](#serializabledata)).\n\nWarning: This method can easily break thread safety if serialized data are mutable (immutability and structs are your friends here).\n\n#### Mappable\n\n```Swift\nprotocol Mappable: Serializable, Deserializable {\n\n    mutating func mapping(_ data: inout MappableData) throws\n}\n```\n\n`Mappable` protocol combines both `Deserializable` and `Serializable`. It provides default implementation for `serialize` but `init` needs to be implemented by hand, usually like this:\n\n```Swift\nstruct SomeObject: Mappable {\n\n\tinit(_ data: DeserializableData) throws {\n\t\ttry mapping(data)\n\t}\n\n\t...\n```\n\nThis also means that the object has to be initialized before calling the `mapping` method.\n\nIf you change the default implementation of `init` or `serialize`, do not forget to call `try mapping(data)` (in `init`) or `mapping(\u0026data)` (in `serialize`).\n\nIn `mapping` you have access to `MappableData` (see [MappableData](#mappabledata)) which allows you to specify how to map object at one place. For this the fields must be mutable. Immutable fields needs to be defined separately in `init` and `serialize` like so:\n\n```Swift\nstruct SomeObject: Mappable {\n\n\tlet constant: Int?\n\tvar variable: Int?\n\n\tinit(_ data: DeserializableData) throws {\n\t\tconstant = data[\"constant\"].get()\n\n\t\ttry mapping(data)\n\t}\n\n\tfunc serialize(to data: inout SerializableData) {\n\t\tdata[\"constant\"].set(constant)\n\n\t\tmapping(\u0026data)\n\t}\n\n\tmutating func mapping(_ data: inout MappableData) throws {\n\t\tdata[\"variable\"].map(\u0026variable)\n\t}\n}\n```\n\nThrows works the same as in `Deserializable`.\n\nWarning: Same problems with thread safety as in `Serializable`.\n\n### DeserializableData/SerializableData/MappableData\n\nThey are used in corresponding methods in the Map protocols. They provide many overloads of one specific method and a subscript. The subscript is used as a key in a dictionary and it can be nested like so:\n\n```Swift\ndata[\"a\"][\"b\"]\ndata[[\"a\", \"b\"]]\ndata[\"a\", \"b\"]\n```\n\nThese all mean that data corresponds to a dictionary with the key \"a\" which is another dictionary with the key \"b\".\n\nThe specific method has overloads for the same types as `ObjectMapper` with the same behavior (see [ObjectMapper](#objectmapper)) and for each of them there are three choices:\n\n* same as in `ObjectMapper` - works with optional type, nil represents .null\n* `try` - works with non optional type and throws exception if .null is found\n* `or` - works with non optional type and replaces .null with value from or.\n\n#### DeserializableData\n\n`DeserializableData` is used in `init` in `Deserializable`. The method is named `get` and it retrieves values from data. Usage:\n\n```Swift\n\tlet value: Int? = data[\"value\"].get()\n\tlet value: Int = try data[\"value\"].get()\n\tlet value: Int = data[\"value\"].get(or: 0)\n\n\tlet value: X? = data[\"value\"].get(using: XTransformation())\n\tlet value: X = try data[\"value\"].get(using: XTransformation())\n\tlet value: X = data[\"value\"].get(using: XTransformation(), or: X())\n```\n\n#### SerializableData\n\n`SerializableData` is used in `serialize` in `Serializable`. The method is named `set` and it sets values to data. Usage:\n\n```Swift\n\tdata[\"value\"].set(value)\n\n\tdata[\"value\"].set(value, using: XTransformation())\n```\n\nNote: `set` does not have overloads for `try` and `or` (there is no need to because it accepts both optionals and non optionals).  \n\n#### MappableData\n\n`MappableData` is used in `mapping` in `Mappable`. The method is named `map` and it either behaves like `get` or `set` depending on the context. Usage:\n\n```Swift\n\tdata[\"value\"].map(\u0026value) // var value: Int?\n\ttry data[\"value\"].map(\u0026value) // var value: Int\n\tdata[\"value\"].map(\u0026value, or: 0) // var value: Int\n\n\tdata[\"value\"].map(\u0026value, using: XTransformation()) // var value: X?\n\ttry data[\"value\"].map(\u0026value, using: XTransformation()) // var value: X\n\tdata[\"value\"].map(\u0026value, using: XTransformation(), or: X()) // var value: X\n```\n\nNote: `try` and `or` affects result of `map` only in deserialization.\n\n### Transformations\n\nTransformations provide another way of specifying how an object should be mapped. They are used to either override behavior of methods in the Map protocol or to allow mapping of type which does not conform to the Map protocol.\n\nThere are three types of them: `DeserializableTransformation` (only for deserialization), `SerializableTransformation` (only for serialization) and `Transformation` (both). Also all of the specialized implementations (`AnyTransformation`, `SupportedTypeConvertible`, ...) have three versions with corresponding name.\n\nBest way to learn how to create a new one is to look at [existing code](Source/Core/Transformation/Transformations).\n\n#### Pre-implemented Transformations\n\n* `EnumTransformation` - uses `RawRepresentable`\n* `URLTransformation` - `String` to `NSURL` (using of relative or absolute path can be specified in `init`)\n\n**Date types**\n\n* `CustomDateFormatTransformation` - `init` with formatString used as `NSDateFormatter.dateFormat`\n* `DateFormatterTransformation` - `init` with `NSDateFormatter`\n* `DateTransformation` - `Double` as timeIntervalSince1970\n* `ISO8601DateTransformation` - `String` in ISO8601 format\n\n**Value types**\n\n* `BoolTransformation`\n* `DoubleTransformation`\n* `IntTranformation`\n* `StringTransformation`\n\n#### AnyTransformation\n\n`AnyTransformation` represents Swift pattern for using protocols with associated types as variable types. To convert any instance of `Transformation` to it, simply call `transformation.typeErased()`. This is often needed in specialized implementations of `Transformation` mentioned below.\n\nNote: `AnyTransformation` has variants for only deserialization or serialization, which also have method `typeErased()`. So sometimes it may be necessary to specify output type of this method explicitly. For example:\n\n```Swift\nlet transformation = IntTransformation()\n\nlet anyTransformation: AnyTransformation = transformation.typeErased()\nlet anyDeserializableTransformation: AnyDeserializableTransformation = transformation.typeErased()\nlet anySerializableTransformation: AnySerializableTransformation = transformation.typeErased()\n```\n\n#### SupportedTypeConvertible\n\nExtending type with `SupportedTypeConvertible` provides default implementation of the Map protocol if there already is `Transformation` for that type. All value types with transformations and `NSURL` conforms to this protocol.\n\nHere is an example implementation for `Int`:\n\n```Swift\nextension Int: SupportedTypeConvertible {\n\n    static var defaultTransformation = IntTransformation().typeErased()\n}\n```\n\nNote: This allows types like `Int` to be used directly in `ObjectMapper` without need to pass the transformation.\n\n#### CompositeTransformation\n\n`CompositeTransformation` allows you to reuse already existing `Transformation` to transform the value of the type `TransitiveObject` to/from `SupportedType`. Then you only need to write code for converting that `TransitiveObject` to/from `Object`.\n\n#### DelegatedTransformation\n\n`DelegatedTransformation` is similar to `CompositeTransformation` in that it uses another `Transformation`, but there is no other conversion after that. Typically this is used to specialize more generic `Transformation`. For example this is implementation of `ISO8601DateTransformation`:\n\n```Swift\nstruct ISO8601DateTransformation: DelegatedTransformation {\n\n    typealias Object = Date\n\n    let transformationDelegate = CustomDateFormatTransformation(formatString: \"yyyy-MM-dd'T'HH:mm:ssZZZZZ\").typeErased()\n}\n```\n\nBecause there is already `CustomDateFormatTransformation` which handles transformation to/from `.string`, it is not necessary to implement it again here. It is sufficient to specify the format used.\n\n### Polymorph\n\n```Swift\nprotocol Polymorph {\n\n    /// Returns type to which the supportedType should be deserialized.\n    func polymorphType\u003cT\u003e(for type: T.Type, in supportedType: SupportedType) -\u003e T.Type\n\n    /// Write info about the type to supportedType if necessary.\n    func writeTypeInfo\u003cT\u003e(to supportedType: inout SupportedType, of type: T.Type)\n}\n```\n\n`Polymorph` represents an object that can decide (at runtime) to which object should be the data deserialized and what metadata should be kept about the object concrete type when being serialized to `SupportedType`.\n\nTo use `Polymorph` initialize `ObjectMapper` with it. For example: \n\n```Swift\nlet objectMapper = ObjectMapper(polymorph: StaticPolymorph())\n```\n\nThere is, at the moment, only one implementation (`StaticPolymorph`) but once Swift adds reflexion, we will implement new one (dynamic). Also feel free to implement your own polymorphism if ours is not universal enough for you. In extreme cases you may even want to \"hardcode\" which types should be used.\n\nExample of what can polymorph do: \n\n```Swift\nclass A: Mappable {\n\n\tlet value: Int?\n\t...\n}\n\nclass B: A {\n\n\tlet text: String?\n\t...\n}\n\nstruct MyPolymorph: Polymorph {\n    \n    // If B is castable to T and supportedType contains a dictionary with key \"type\" and the value \"B\", then the type to use is `B`, otherwise does nothing.\n    func polymorphType\u003cT\u003e(for type: T.Type, in supportedType: SupportedType) -\u003e T.Type {\n        if let bType = B.self as? T.Type, supportedType.dictionary?[\"type\"]?.string == \"B\" {\n            return bType\n        }\n        return type\n    }\n    \n    // If T is B, write info about it into supportedType.\n    func writeTypeInfo\u003cT\u003e(to supportedType: inout SupportedType, of type: T.Type) {\n        if type == B.self {\n            supportedType.addToDictionary(key: \"type\", value: .string(\"B\"))\n        }\n    }\n}\n\nlet objectMapper = ObjectMapper()\nlet objectMapperWithPolymorh = ObjectMapper(polymorph: MyPolymorph())\n\nlet aType: SupportedType = .dictionary([\"value\": .int(1)])\nlet bType: SupportedType = .dictionary([\"value\": .int(2), \"text\": .string(\"text\"), \"type\": .string(\"B\")])\n\n// Deserialization\nlet aObject: A? = objectMapper.deserialize(aType) // A(value: 1) - no surprise here\nlet bObject: A? = objectMapper.deserialize(bType) // A(value: 2) - the rest of the dictionary is ignored\n\nlet aPolymorphic: A? = objectMapperWithPolymorh.deserialize(aType) // A(value: 1) - again the same result\nlet bPolymorphic: A? = objectMapperWithPolymorh.deserialize(bType) // B(value: 2, text: \"text\") - this time the polymorph comes into play\n\n// Serialization\nobjectMapper.serialize(aObject) // .dictionary([\"value\": .int(1)])\nobjectMapper.serialize(bObject) // .dictionary([\"value\": .int(2)])\nobjectMapperWithPolymorh.serialize(aPolymorphic) // .dictionary([\"value\": .int(1)]) - so far no difference\n\nobjectMapper.serialize(bPolymorphic) // .dictionary([\"value\": .int(2), \"text\": .string(\"text\")])\nobjectMapperWithPolymorh.serialize(bPolymorphic) // .dictionary([\"value\": .int(2), \"text\": .string(\"text\"), \"type\": .string(\"B\")]) - type is added\n```\n\n#### StaticPolymorph\n\n`StaticPolymorph` resolves types by looking into `SupportedType` for dictionary entries with the specific key (which key is used is determined by object type at input). Then the value for that key is compared to names of known types. If match is found than the correct type is return otherwise it returns the input type. When serializing the `StaticPolymorh` adds into `SupportedType` the key value pair that corresponds to the serialized type.\n\n`StaticPolymorph` affects only objects which implement the `Polymorphic` protocol. For other types `polymorphType` returns the input type and `writeTypeInfo` does nothing.\n\nNote: Implementing `Polymoprhic` is not enough for an object to be used in `ObjectMapper`. To solve this, there are type aliases that combines `Polymorhic` with the Map protocol: `PolymorphicDeserializable`, `PolymorphicSerializable` and `PolymorphicMappable`.\n\nNote: Limitation of `StaticPolymorph` is that only classes can be used. It is not possible to use a protocol and structs.\n\n**Polymorphic**\n\n`Polymorphic` is defined like this:\n\n```Swift\nprotocol Polymorphic: AnyObject {\n\n    static var polymorphicKey: String { get }\n\n    static var polymorphicInfo: PolymorphicInfo { get }\n}\n```\n\n`polymorphicKey` represents the key mentioned above. (Where to look for a name of the type.) `polymorphicKey` can be overriden. That allows each type to be identified with the key and name combination. It is not defined what happens if more than one key is present in `SupportedType` with valid names! There can be multiple subtypes with the same key or name as long as the combination is unique.\n\n`polymorphicInfo` defines the type name and its subtypes (they don't have to be direct subtypes). It cannot be checked if these types are really subtypes but this won't be a problem if you use `GenericPolymorphicInfo` which does that check. If registered subtype is not a real subtype then `StaticPolymorph` will ignore it (but don't rely on this behavior). When `StaticPolymorph` resolves subtypes it relies only on information provided by `polymorphicInfo`, so subtypes which are not registered in input type (or in registered subtype) don't exist for it. To prevent potential misuse it is prohibited to use `Polymorphic` as input type if it does not override `polymorphicInfo` (as is seen in the example below).\n\n`Polymorphic` provides method `createPolymorphicInfo()` which returns `GenericPolymorphicInfo`. This method has optional parameter `name` which represents the polymorphic name of the type (default value the is real name of the type). `GenericPolymorphicInfo` allows you to register subtypes with overloads of `register()` and `with()` (`with()` returns `self` to allow chaining).\n\n**Example**\n\n```Swift\nclass A: Polymorphic {\n\n    class var polymorphicKey: String {\n        return \"K\"\n    }\n\n    class var polymorphicInfo: PolymorphicInfo {\n        return createPolymorphicInfo(name: \"Base\").with(subtypes: B.self, D.self)\n    }\n}\n\nclass B: A {\n\n    override class var polymorphicInfo: PolymorphicInfo {\n        return createPolymorphicInfo().with(subtype: C.self)\n    }\n}\n\nclass C: B {\n\n\toverride class var polymorphicKey: String {\n        return \"C\"\n    }\n}\n\nclass D: C {\n\n    override class var polymorphicInfo: PolymorphicInfo {\n        return createPolymorphicInfo()\n    }\n}\n```\n\nNote: This example omits implementation of the Map protocol.\n\nThere are few things to notice.\n\n1. `C` overrides `polymorphicKey` that means: `A` and `B` have the key \"K\" and `C` and `D` have the key \"C\". So `SupportedType.dictionary([\"C\": .string(\"C\")]` represents `C` but `SupportedType.dictionary([\"K\": .string(\"C\")]` means nothing in this context.\n1. `A` has explicit name \"Base\". So `SupportedType.dictionary([\"K\": .string(\"Base\")]` represents `A`.\n1. `C` does not override `polymorphicInfo`. This means that `C` cannot be used as the input type (exception will be raised) but `D` can be, even though it won't ever resolve to another type.\n1. `D` is registered in `A` not `B`. Because of that, `B` does not know about `D`. So if `B` is the input type, you can never get `D` as subtype.\n1. `A` knows about `C` because it is register in `B` which is registered in `A`.\n\n### Thread safety\n\nDataMapper is designed to be used on the background thread (default implementation is thread safe). If you want to use it that way you need to make sure that implementations of all methods from the Map protocols are thread safe as well (or that the objects you are using cannot be used at two threads simultaneously). Your custom implementations of protocols like `Serializer`, `Polymorph`, `Transformation` etc. must be thread safe too.\n\n## Versioning\n\nThis library uses semantic versioning. Until the version 1.0 API breaking changes may occur even in minor versions. We consider the version 0.1 to be prerelease, which means that API should be stable but is not tested yet in a real project. After that testing, we make needed adjustments and bump the version to 1.0 (first release).\n\n## Author\n\n* Tadeas Kriz, [tadeas@brightify.org](mailto:tadeas@brightify.org)\n* Filip Dolník, [filip@brightify.org](mailto:filip@brightify.org)\n\n## Used libraries in tests\n\n* [Quick](https://github.com/Quick/Quick)\n* [Nimble](https://github.com/Quick/Nimble)\n\n## License\n\nDataMapper is available under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrightify%2Fdatamapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrightify%2Fdatamapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrightify%2Fdatamapper/lists"}