Convert Figma logo to code with AI

tristanhimmelman logoObjectMapper

Simple JSON Object mapping written in Swift

9,158
1,032
9,158
68

Top Related Projects

Simple JSON Object mapping written in Swift

The better way to deal with JSON data in Swift.

Type-erased wrappers for Encodable, Decodable, and Codable values

A handy swift json-object serialization/deserialization library

Simple Swift wrapper for Keychain that works on iOS, watchOS, tvOS and macOS.

Quick Overview

ObjectMapper is a framework for Swift that makes it easy to convert model objects (classes and structs) to and from JSON. It uses a simple mapping system to define the relationship between keys in JSON and properties of your Swift objects, allowing for seamless serialization and deserialization.

Pros

  • Easy to use with a clean, intuitive API
  • Supports custom transformations for complex property types
  • Works with both classes and structs
  • Integrates well with Alamofire for network requests

Cons

  • Performance can be slower compared to manual parsing for large datasets
  • Requires manual mapping setup for each model
  • May not handle very complex nested JSON structures as elegantly as some alternatives
  • Limited support for other data formats besides JSON

Code Examples

  1. Basic mapping:
struct User: Mappable {
    var name: String?
    var age: Int?
    
    init?(map: Map) {}
    
    mutating func mapping(map: Map) {
        name <- map["name"]
        age <- map["age"]
    }
}
  1. Custom transformation:
let dateTransform = DateTransform()
var date: Date?
date <- (map["date"], dateTransform)
  1. Nested objects:
struct User: Mappable {
    var name: String?
    var address: Address?
    
    init?(map: Map) {}
    
    mutating func mapping(map: Map) {
        name <- map["name"]
        address <- map["address"]
    }
}

struct Address: Mappable {
    var street: String?
    var city: String?
    
    init?(map: Map) {}
    
    mutating func mapping(map: Map) {
        street <- map["street"]
        city <- map["city"]
    }
}

Getting Started

  1. Install ObjectMapper using CocoaPods by adding to your Podfile:

    pod 'ObjectMapper', '~> 4.2'
    
  2. Import ObjectMapper in your Swift file:

    import ObjectMapper
    
  3. Create a model that conforms to the Mappable protocol:

    struct User: Mappable {
        var id: Int?
        var name: String?
        
        init?(map: Map) {}
        
        mutating func mapping(map: Map) {
            id <- map["id"]
            name <- map["name"]
        }
    }
    
  4. Use ObjectMapper to convert JSON to objects or vice versa:

    // JSON to object
    let user = Mapper<User>().map(JSONString: jsonString)
    
    // Object to JSON
    let jsonString = Mapper().toJSONString(user)
    

Competitor Comparisons

Simple JSON Object mapping written in Swift

Pros of ObjectMapper

  • Simplified JSON parsing and mapping to Swift objects
  • Supports custom transformations for complex data types
  • Extensive documentation and community support

Cons of ObjectMapper

  • May require more setup for complex object structures
  • Performance can be slower for large datasets compared to some alternatives
  • Limited support for certain Swift-specific features like Codable

Code Comparison

ObjectMapper:

class User: Mappable {
    var name: String?
    var age: Int?
    
    required init?(map: Map) {}
    
    func mapping(map: Map) {
        name <- map["name"]
        age <- map["age"]
    }
}

Note: The comparison requested is between the same repository (tristanhimmelman/ObjectMapper). As there is no other repository to compare it with, the pros, cons, and code comparison are provided for ObjectMapper itself, highlighting its features and potential drawbacks. A proper comparison would require a different repository to compare against.

The better way to deal with JSON data in Swift.

Pros of SwiftyJSON

  • Simpler syntax for parsing JSON, especially for nested structures
  • Better handling of optional values and type safety
  • Faster JSON parsing performance in some scenarios

Cons of SwiftyJSON

  • Limited support for custom object mapping
  • Requires more manual work for complex data structures
  • Less flexibility in handling different data types

Code Comparison

SwiftyJSON:

let name = json["user"]["name"].stringValue
let age = json["user"]["age"].intValue

ObjectMapper:

struct User: Mappable {
    var name: String?
    var age: Int?
    
    mutating func mapping(map: Map) {
        name <- map["name"]
        age <- map["age"]
    }
}

Key Differences

  • SwiftyJSON focuses on easy JSON parsing and manipulation
  • ObjectMapper specializes in object mapping and serialization
  • SwiftyJSON is more suitable for simple JSON structures
  • ObjectMapper excels in handling complex object hierarchies

Use Cases

  • Choose SwiftyJSON for quick JSON parsing and simple data structures
  • Opt for ObjectMapper when working with complex object models or when serialization is required

Community and Maintenance

  • Both projects have active communities and regular updates
  • SwiftyJSON has more stars and forks on GitHub
  • ObjectMapper offers more extensive documentation and examples

Type-erased wrappers for Encodable, Decodable, and Codable values

Pros of AnyCodable

  • Lightweight and focused solely on encoding/decoding Any type
  • Seamless integration with Swift's Codable protocol
  • Simple API with minimal setup required

Cons of AnyCodable

  • Limited functionality compared to ObjectMapper's broader feature set
  • Lacks custom mapping and transformation options
  • May require additional code for complex object mappings

Code Comparison

ObjectMapper:

class User: Mappable {
    var name: String?
    var age: Int?
    
    required init?(map: Map) {}
    
    func mapping(map: Map) {
        name <- map["name"]
        age <- map["age"]
    }
}

AnyCodable:

struct User: Codable {
    var name: String?
    var age: Int?
}

let user = try JSONDecoder().decode(User.self, from: jsonData)

ObjectMapper provides a more explicit mapping process, while AnyCodable leverages Swift's built-in Codable protocol for simpler, more concise code. ObjectMapper offers greater flexibility for custom mappings, but AnyCodable integrates more seamlessly with Swift's standard library and requires less boilerplate code for basic use cases.

A handy swift json-object serialization/deserialization library

Pros of HandyJSON

  • Supports pure Swift types without @objc annotation
  • Allows custom mapping without manual implementation
  • Generally faster performance, especially for large datasets

Cons of HandyJSON

  • Less mature and less widely adopted than ObjectMapper
  • Limited support for complex nested structures
  • Fewer features and customization options

Code Comparison

HandyJSON:

struct User: HandyJSON {
    var name: String?
    var age: Int?
}

let jsonString = "{\"name\":\"Bob\",\"age\":25}"
if let user = User.deserialize(from: jsonString) {
    print(user.name ?? "", user.age ?? 0)
}

ObjectMapper:

class User: Mappable {
    var name: String?
    var age: Int?
    
    required init?(map: Map) {}
    
    func mapping(map: Map) {
        name <- map["name"]
        age <- map["age"]
    }
}

if let user = Mapper<User>().map(JSONString: jsonString) {
    print(user.name ?? "", user.age ?? 0)
}

HandyJSON offers a simpler syntax for defining mappable objects, requiring less boilerplate code. ObjectMapper, on the other hand, provides more control over the mapping process through its mapping function. Both libraries achieve similar results, but HandyJSON's approach is more concise and Swift-like.

Simple Swift wrapper for Keychain that works on iOS, watchOS, tvOS and macOS.

Pros of KeychainAccess

  • Focused specifically on Keychain operations, providing a more streamlined API for this purpose
  • Supports synchronization across devices using iCloud Keychain
  • Offers a simpler, more intuitive interface for Keychain interactions

Cons of KeychainAccess

  • Limited to Keychain operations, lacking the broader JSON mapping capabilities of ObjectMapper
  • May require additional libraries for complex data transformations or mappings

Code Comparison

KeychainAccess:

let keychain = Keychain(service: "com.example.app")
keychain["username"] = "john_doe"
let username = keychain["username"]

ObjectMapper:

struct User: Mappable {
    var username: String?
    
    init?(map: Map) {}
    
    mutating func mapping(map: Map) {
        username <- map["username"]
    }
}

KeychainAccess provides a straightforward key-value interface for storing and retrieving data from the Keychain. ObjectMapper, on the other hand, focuses on mapping JSON data to Swift objects, offering a more complex but flexible approach for data transformation.

While KeychainAccess excels in secure data storage, ObjectMapper shines in JSON parsing and object mapping scenarios. The choice between the two depends on the specific requirements of your project, with KeychainAccess being ideal for simple, secure storage needs and ObjectMapper better suited for complex data transformations and API integrations.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

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:

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    <- map["username"]
        age         <- map["age"]
        weight      <- map["weight"]
        array       <- map["arr"]
        dictionary  <- map["dict"]
        bestFriend  <- map["best_friend"]
        friends     <- map["friends"]
        birthday    <- (map["birthday"], DateTransform())
    }
}

struct Temperature: Mappable {
    var celsius: Double?
    var fahrenheit: Double?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        celsius 	<- map["celsius"]
        fahrenheit 	<- map["fahrenheit"]
    }
}

Once your class implements Mappable, ObjectMapper allows you to easily convert to and from JSON.

Convert a JSON string to a model object:

let user = User(JSONString: JSONString)

Convert a model object to a JSON string:

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):

// Convert JSON String to Model
let user = Mapper<User>().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<Any>
  • Dictionary<String, Any>
  • Object<T: Mappable>
  • Array<T: Mappable>
  • Array<Array<T: Mappable>>
  • Set<T: Mappable>
  • Dictionary<String, T: Mappable>
  • Dictionary<String, Array<T: Mappable>>
  • 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:

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   <- map["id"]
  name <- map["name"]
}
Model -> JSON
func mapping(map: Map) {
  id   >>> map["id"]
  name >>> map["name"]
}
mutating func mapping(map: Map) {
  id   <- map["id"]
  name <- map["name"]
}
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:

"distance" : {
     "text" : "102 ft",
     "value" : 31
}

You can access the nested objects as follows:

func mapping(map: Map) {
    distance <- map["distance.value"]
}

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:

distance <- map["distances.0.value"]

If you have a key that contains ., you can individually disable the above feature as follows:

func mapping(map: Map) {
    identifier <- map["app.identifier", nested: false]
}

When you have nested keys which contain ., you can pass the custom nested key delimiter as follows (#629):

func mapping(map: Map) {
    appName <- map["com.myapp.info->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:

birthday <- (map["birthday"], DateTransform())

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:

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:

let transform = TransformOf<Int, String>(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:

id <- (map["id"], TransformOf<Int, String>(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 <- map["base"]
    }
}

class Subclass: Base {
    var sub: String?

    required init?(map: Map) {
        super.init(map)
    }

    override func mapping(map: Map) {
        super.mapping(map)
        
        sub <- map["sub"]
    }
}

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:

class Result<T: Mappable>: Mappable {
    var result: T?

    required init?(map: Map){

    }

    func mapping(map: Map) {
        result <- map["result"]
    }
}

let result = Mapper<Result<User>>().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.

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<User>(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 <- 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.