Skip to content

Instantly share code, notes, and snippets.

@Pretz

Pretz/swift-json.md

Last active Jun 21, 2017
Embed
What would you like to do?
A comparison of different JSON Object Mapping Libraries for Swift

Swift JSON Object Mappers

Argo

struct User {
  let id: Int
  let name: String
  let email: String?
  let role: Role
  let companyName: String
  let friends: [User]
}

extension User: Decodable {
  static func decode(j: JSON) -> Decoded<User> {
    return curry(User.init)
      <^> j <| "id"
      <*> j <| "name"
      <*> j <|? "email" // Use ? for parsing optional values
      <*> j <| "role" // Custom types that also conform to Decodable just work
      <*> j <| ["company", "name"] // Parse nested objects
      <*> j <|| "friends" // parse arrays of objects
  }
}

// Wherever you receive JSON data:

let json: AnyObject? = try? NSJSONSerialization.JSONObjectWithData(data, options: [])

if let j: AnyObject = json {
  let user: User? = decode(j)
}

ObjectMapper

class User: Mappable {
    var username: String?
    var age: Int?
    var weight: Double!
    var array: [AnyObject]?
    var dictionary: [String : AnyObject] = [:]
    var bestFriend: User?                       // Nested User object
    var friends: [User]?                        // Array of Users
    var birthday: NSDate?

    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())
    }
}

let user = Mapper<User>().map(JSONString)

Decodable

struct Repository {
    let name: String
    let description: String
    let stargazersCount: Int
    let language: String?
    let sometimesMissingKey: String?

    let owner: User // Struct conforming to Decodable
    let defaultBranch: Branch // Struct NOT conforming to Decodable

    var fullName: String { return "\(owner.login)/\(name)" }
}

extension Repository: Decodable {
    static func decode(j: AnyObject) throws -> Repository {
        return try Repository(
                    name: j => "name", 
                    description: j => "description", 
                    stargazersCount: j => "stargazers_count", 
                    language: j => "language", 
                    sometimesMissingKey: try? j => "sometimesMissingKey",
                    owner: j => "owner", 
                    defaultBranch: Branch(name: j => "default_branch")
                )
    }
}

Unbox

struct User: Unboxable {
    let name: String
    let age: Int

    init(unboxer: Unboxer) {
        self.name = unboxer.unbox("name")
        self.age = unboxer.unbox("age")
    }
}

let user: User? = Unbox(dictionary)

Genome

enum PetType : String {
    case Dog = "dog"
    case Cat = "cat"
}

struct Pet {
      var name: String = ""
      var type: PetType!
      var nickname: String?
}
extension Pet : BasicMappable {
    mutating func sequence(map: Map) throws {
        try name <~> map["name"]
        try nickname <~> map["nickname"]
        try type <~> map["type"]
            .transformFromJson {
                return PetType(rawValue: $0)
            }
            .transformToJson {
                return $0.rawValue
            }
    }
}

do {
    let rover = try Pet.mappedInstance(json_rover)
    print(rover)
} catch {
    print(error)
}

ModelRocket

class Vehicle: Model {
    let make  = Property<String>(key: "make")
    let model = Property<String>(key: "model", required: true)
    let year  = Property<Int>(key: "year") { year in
        if year < 2015 {
            // offer discount
        }
    }
    let color = Property<UIColor>(key: "color", defaultValue: UIColor.blackColor())
}

// instantiate object
let vehicle = Vehicle(json: json)

// get property type
println("Vehicle make property has type: \(vehicle.make.type)")

// get property value
if let make = vehicle.make.value {
    println("Vehicle make: \(make)")
}

Tailor

struct Person: Mappable {

  var firstName: String? = ""
  var lastName: String? = ""
  var spouse: Person?

  init(_ map: JSONDictionary) {
    firstName <- map.property("first_name")
    lastName  <- map.property("last_name")
    spouse    <- map.object("spouse")
  }
}

let dictionary = [
  "first_name" : "Taylor", 
  "last_name" : "Swift",
  "spouse" : ["first_name" : "Calvin", 
              "last_name" : "Harris"]
]
let model = Person(dictionary)

Gloss

struct Repo: Decodable {

    let repoId: Int?
    let name: String?
    let desc: String?
    let url: NSURL?
    let owner: RepoOwner?
    let primaryLanguage: Language?

    enum Language: String {
        case Swift = "Swift"
        case ObjectiveC = "Objective-C"
    }

    // MARK: - Deserialization

    init?(json: JSON) {
        self.repoId = "id" <~~ json
        self.name = "name" <~~ json
        self.desc = "description" <~~ json
        self.url = "html_url" <~~ json
        self.owner = "owner" <~~ json
        self.primaryLanguage = "language" <~~ json
    }
}

guard let repoOwner = Repo(json: repoJSON)
    else { /* handle nil object here */ }

JSONCodable

struct User {
    let id: Int
    let name: String
    var email: String?
    var company: Company?
    var friends: [User] = []
}
extension User: JSONDecodable {
    init?(JSONDictionary: JSONObject) {
        let decoder = JSONDecoder(object: JSONDictionary)
        do {
            id = try decoder.decode("id")
            name = try decoder.decode("full_name")
            email = try decoder.decode("email")
            company = try decoder.decode("company")
            friends = try decoder.decode("friends")
        }
        catch {
            return nil
        }
    }
}
let user = User(JSONDictionary: JSON)
@alanf

This comment has been minimized.

Copy link

@alanf alanf commented Feb 22, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment