ObjectMapper

by tristanhimmelman

tristanhimmelman / ObjectMapper

Simple JSON Object mapping written in Swift

8.6K Stars 970 Forks Last release: 5 months ago (4.2.0) MIT License 1.1K Commits 60 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

ObjectMapper

CocoaPods Carthage compatible Swift Package Manager Build Status

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from JSON.

Features:

  • Mapping JSON to objects
  • Mapping objects to JSON
  • Nested Objects (stand alone, in arrays or in dictionaries)
  • Custom transformations during mapping
  • Struct support
  • Immutable support

The Basics

To support mapping, a class or struct just needs to implement the

Mappable
protocol which includes the following functions:
swift
init?(map: Map)
mutating func mapping(map: Map)
ObjectMapper uses the
 operator to define how each member variable maps to and from JSON.
class User: Mappable {
    var username: String?
    var age: Int?
    var weight: Double!
    var array: [Any]?
    var dictionary: [String : Any] = [:]
    var bestFriend: User?                       // Nested User object
    var friends: [User]?                        // Array of Users
    var birthday: Date?

required init?(map: Map) {

}

// Mappable
func mapping(map: Map) {
    username    

Once your class implements

Mappable
, ObjectMapper allows you to easily convert to and from JSON.

Convert a JSON string to a model object:

swift
let user = User(JSONString: JSONString)

Convert a model object to a JSON string:

swift
let JSONString = user.toJSONString(prettyPrint: true)

Alternatively, the

Mapper.swift
class can also be used to accomplish the above (it also provides extra functionality for other situations):
swift
// Convert JSON String to Model
let user = Mapper().map(JSONString: JSONString)
// Create JSON String from Model
let JSONString = Mapper().toJSONString(user, prettyPrint: true)

ObjectMapper can map classes composed of the following types: -

Int
-
Bool
-
Double
-
Float
-
String
-
RawRepresentable
(Enums) -
Array
-
Dictionary
-
Object
-
Array
-
Array>
-
Set
-
Dictionary
-
Dictionary>
- Optionals of all the above - Implicitly Unwrapped Optionals of the above

Mappable
Protocol

mutating func mapping(map: Map)

This function is where all mapping definitions should go. When parsing JSON, this function is executed after successful object creation. When generating JSON, it is the only function that is called on the object.

init?(map: Map)

This failable initializer is used by ObjectMapper for object creation. It can be used by developers to validate JSON prior to object serialization. Returning nil within the function will prevent the mapping from occuring. You can inspect the JSON stored within the

Map
object to do your validation:
swift
required init?(map: Map){
    // check if a required "name" property exists within the JSON.
    if map.JSON["name"] == nil {
        return nil
    }
}

StaticMappable
Protocol

StaticMappable
is an alternative to
Mappable
. It provides developers with a static function that is used by ObjectMapper for object initialization instead of
init?(map: Map)
.

Note:

StaticMappable
, like
Mappable
, is a sub protocol of
BaseMappable
which is where the
mapping(map: Map)
function is defined.

static func objectForMapping(map: Map) -> BaseMappable?

ObjectMapper uses this function to get objects to use for mapping. Developers should return an instance of an object that conforms to

BaseMappable
in this function. This function can also be used to: - validate JSON prior to object serialization - provide an existing cached object to be used for mapping - return an object of another type (which also conforms to
BaseMappable
) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for mapping (see examples in ClassClusterTests.swift)

If you need to implement ObjectMapper in an extension, you will need to adopt this protocol instead of

Mappable
.

ImmutableMappable
Protocol

ImmutableMappable
provides the ability to map immutable properties. This is how
ImmutableMappable
differs from
Mappable
:

ImmutableMappable Mappable
Properties

let id: Int
let name: String?
var id: Int!
var name: String?
JSON -> Model
init(map: Map) throws {
  id   = try map.value("id")
  name = try? map.value("name")
}
mutating func mapping(map: Map) {
  id   
    
Model -> JSON
func mapping(map: Map) {
  id   >>> map["id"]
  name >>> map["name"]
}
mutating func mapping(map: Map) {
  id   
    
Initializing
try User(JSONString: JSONString)
User(JSONString: JSONString)

init(map: Map) throws

This throwable initializer is used to map immutable properties from the given

Map
. Every immutable property should be initialized in this initializer.

This initializer throws an error when: -

Map
fails to get a value for the given key -
Map
fails to transform a value using
Transform

ImmutableMappable
uses
Map.value(_:using:)
method to get values from the
Map
. This method should be used with the
try
keyword as it is throwable.
Optional
properties can easily be handled using
try?
.
init(map: Map) throws {
    name      = try map.value("name") // throws an error when it fails
    createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails
    updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional
    posts     = (try? map.value("posts")) ?? [] // optional + default value
    surname    = try? map.value("surname", default: "DefaultSurname") // optional + default value as an argument
}

mutating func mapping(map: Map)

This method is where the reverse transform is performed (model to JSON). Since immutable properties cannot be mapped with the

 operator, developers have to define the reverse transform using the 
>>>
operator.
mutating func mapping(map: Map) {
    name      >>> map["name"]
    createdAt >>> (map["createdAt"], DateTransform())
    updatedAt >>> (map["updatedAt"], DateTransform())
    posts     >>> map["posts"]
}

Easy Mapping of Nested Objects

ObjectMapper supports dot notation within keys for easy mapping of nested objects. Given the following JSON String:

json
"distance" : {
     "text" : "102 ft",
     "value" : 31
}
You can access the nested objects as follows:
swift
func mapping(map: Map) {
    distance 
Nested keys also support accessing values from an array. Given a JSON response with an array of distances, the value could be accessed as follows:
swift
distance 
If you have a key that contains 
.
, you can individually disable the above feature as follows:
swift
func mapping(map: Map) {
    identifier 
When you have nested keys which contain 
.
, you can pass the custom nested key delimiter as follows (#629):
swift
func mapping(map: Map) {
    appName com.myapp.name", delimiter: "->"]
}

Custom Transforms

ObjectMapper also supports custom transforms that convert values during the mapping process. To use a transform, simply create a tuple with

map["field_name"]
and the transform of your choice on the right side of the
 operator:
swift
birthday 
The above transform will convert the JSON Int value to an Date when reading JSON and will convert the Date to an Int when converting objects to JSON.

You can easily create your own custom transforms by adopting and implementing the methods in the

TransformType
protocol: ```swift public protocol TransformType { associatedtype Object associatedtype JSON
func transformFromJSON(_ value: Any?) -> Object?
func transformToJSON(_ value: Object?) -> JSON?

} ```

TransformOf

In a lot of situations you can use the built-in transform class

TransformOf
to quickly perform a desired transformation.
TransformOf
is initialized with two types and two closures. The types define what the transform is converting to and from and the closures perform the actual transformation.

For example, if you want to transform a JSON

String
value to an
Int
you could use
TransformOf
as follows: ```swift let transform = TransformOf(fromJSON: { (value: String?) -> Int? in // transform value from String? to Int? return Int(value!) }, toJSON: { (value: Int?) -> String? in // transform value from Int? to String? if let value = value { return String(value) } return nil })

id <- (map["id"], transform)

Here is a more condensed version of the above:
swift id <- (map["id"], TransformOf(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } })) ```

Subclasses

Classes that implement the

Mappable
protocol can easily be subclassed. When subclassing mappable classes, follow the structure below:
class Base: Mappable {
    var base: String?

required init?(map: Map) {

}

func mapping(map: Map) {
    base 

Make sure your subclass implementation calls the right initializers and mapping functions to also apply the mappings from your superclass.

Generic Objects

ObjectMapper can handle classes with generic types as long as the generic type also conforms to

Mappable
. See the following example: ```swift class Result: Mappable { var result: T?
required init?(map: Map){

}

func mapping(map: Map) { result

}

let result = Mapper>().map(JSON) ```

Mapping Context

The

Map
object which is passed around during mapping, has an optional
MapContext
object that is available for developers to use if they need to pass information around during mapping.

To take advantage of this feature, simply create an object that implements

MapContext
(which is an empty protocol) and pass it into
Mapper
during initialization. ```swift struct Context: MapContext { var importantMappingInfo = "Info that I need during mapping" }

class User: Mappable { var name: String?

required init?(map: Map){

}

func mapping(map: Map){ if let context = map.context as? Context { // use context to make decisions about mapping } }

}

let context = Context() let user = Mapper(context: context).map(JSONString) ```

ObjectMapper + Alamofire

If you are using Alamofire for networking and you want to convert your responses to Swift objects, you can use AlamofireObjectMapper. It is a simple Alamofire extension that uses ObjectMapper to automatically map JSON response data to Swift objects.

ObjectMapper + Realm

ObjectMapper and Realm can be used together. Simply follow the class structure below and you will be able to use ObjectMapper to generate your Realm models:

class Model: Object, Mappable {
    dynamic var name = ""

required convenience init?(map: Map) {
    self.init()
}

func mapping(map: Map) {
    name 

If you want to serialize associated RealmObjects, you can use ObjectMapper+Realm. It is a simple Realm extension that serializes arbitrary JSON into Realm's

List
class.

To serialize Swift

String
,
Int
,
Double
and
Bool
arrays you can use ObjectMapperAdditions/Realm. It'll wrap Swift types into RealmValues that can be stored in Realm's
List
class.

Note: Generating a JSON string of a Realm Object using ObjectMappers'

toJSON
function only works within a Realm write transaction. This is because ObjectMapper uses the
inout
flag in its mapping functions (
) which are used both for serializing and deserializing. Realm detects the flag and forces the 
toJSON
function to be called within a write block even though the objects are not being modified.

Projects Using ObjectMapper

If you have a project that utilizes, extends or provides tooling for ObjectMapper, please submit a PR with a link to your project in this section of the README.

To Do

  • Improve error handling. Perhaps using
    throws
  • Class cluster documentation

Contributing

Contributions are very welcome 👍😃.

Before submitting any pull request, please ensure you have run the included tests and they have passed. If you are including new functionality, please write test cases for it as well.

Installation

Cocoapods

ObjectMapper can be added to your project using CocoaPods 0.36 or later by adding the following line to your

Podfile
:
pod 'ObjectMapper', '~> 3.5' (check releases to make sure this is the latest version)

Carthage

If you're using Carthage you can add a dependency on ObjectMapper by adding it to your

Cartfile
:
github "tristanhimmelman/ObjectMapper" ~> 3.5 (check releases to make sure this is the latest version)

Swift Package Manager

To add ObjectMapper to a Swift Package Manager based project, add:

.package(url: "https://github.com/tristanhimmelman/ObjectMapper.git", .upToNextMajor(from: "4.1.0")),

to your

Package.swift
files
dependencies
array.

Submodule

Otherwise, ObjectMapper can be added as a submodule:

  1. Add ObjectMapper as a submodule by opening the terminal,
    cd
    -ing into your top-level project directory, and entering the command
    git submodule add https://github.com/tristanhimmelman/ObjectMapper.git
  2. Open the
    ObjectMapper
    folder, and drag
    ObjectMapper.xcodeproj
    into the file navigator of your app project.
  3. In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
  4. Ensure that the deployment target of
    ObjectMapper.framework
    matches that of the application target.
  5. In the tab bar at the top of that window, open the "Build Phases" panel.
  6. Expand the "Target Dependencies" group, and add
    ObjectMapper.framework
    .
  7. Click on the
    +
    button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add
    ObjectMapper.framework
    .

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.