How to convert a Swift object to a dictionary

32,633

Solution 1

Swift currently does not support advanced reflection like Java or C# so the answer is: no, there is not an equally easy and automated way with pure Swift.

[Update] Swift 4 has meanwhile the Codable protocol which allows serializing to/from JSON and PLIST.

typealias Codable = Decodable & Encodable

Solution 2

I must disagree with @Darko.

In Swift 2,

use protocol oriented programming and the simple reflection offered by Mirror class :

protocol JSONAble {}

extension JSONAble {
    func toDict() -> [String:Any] {
        var dict = [String:Any]()
        let otherSelf = Mirror(reflecting: self)
        for child in otherSelf.children {
            if let key = child.label {
                dict[key] = child.value
            }
        }
        return dict
    }
}

then you can use this protocol with your request class and produce the desired dictionary :

class JsonRequest : JSONAble {
    var param1 : String?
    // ...
}

let request = JsonRequest()
// set params of the request
let dict = request.toDict()
// use your dict

Solution 3

My solution to this will be something like this:

extension Encodable {

    var dict : [String: Any]? {
        guard let data = try? JSONEncoder().encode(self) else { return nil }
        guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { return nil }
        return json
    }
}

and usage will be something like this:

movies.compactMap { $0.dict }

Solution 4

Without using reflection, and works for nested objects (Swift 4):

protocol Serializable {
    var properties:Array<String> { get }
    func valueForKey(key: String) -> Any?
    func toDictionary() -> [String:Any]
}

extension Serializable {
    func toDictionary() -> [String:Any] {
        var dict:[String:Any] = [:]

        for prop in self.properties {
            if let val = self.valueForKey(key: prop) as? String {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Int {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Double {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Array<String> {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Serializable {
                dict[prop] = val.toDictionary()
            } else if let val = self.valueForKey(key: prop) as? Array<Serializable> {
                var arr = Array<[String:Any]>()

                for item in (val as Array<Serializable>) {
                    arr.append(item.toDictionary())
                }

                dict[prop] = arr
            }
        }

        return dict
    }
}

Just implement properties and valueForKey for the custom objects you want to convert. For example:

class Question {
    let title:String
    let answer:Int

    init(title:String, answer:Int) {
        self.title = title
        self.answer = answer
    }
}
extension Question : Serializable {
    var properties: Array<String> {
        return ["title", "answer"]
    }

    func valueForKey(key: String) -> Any? {
        switch key {
        case "title":
            return title
        case "answer":
            return answer
        default:
            return nil
        }
    }
} 

You can add more value types in the toDictionary function if you need.

Solution 5

You can also use the ObjectMapper library. It has a "toJSON" method that converts your object to a dictionary.

Share:
32,633
JL Gradley
Author by

JL Gradley

Updated on July 16, 2022

Comments

  • JL Gradley
    JL Gradley almost 2 years

    I'm relatively new to iOS programming. However, I would have assumed that Swift would have an automated way of converting objects to JSON and vice versa. That being said, I have found several libraries that can do this.

    HOWEVER...

    It seems that no matter how you post data to a web service (even using something like AlamoFire), the requests must be a dictionary. All these forums show examples of how easy it is to convert the returned JSON string to objects. True. But the request needs to be manually coded. That is, go through all of the object properties and map them as a dictionary.

    So my question is this: Am I missing something? Have I got this all wrong and there's a super-easy way to either (a) send JSON (instead of a dictionary) in the REQUEST or (b) convert an object automatically to a dictionary?

    Again, I see how easy it is to deal with a JSON response. I'm just looking for an automatic way to convert the request object I want to post to a web service into a format that a library like AlamoFire (or whatever) requires. With other languages this is fairly trivial, so I'm hoping there's an equally easy and automated way with Swift.