Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a dictionary be converted to a class or struct in Swift?

I am working on a native iOS application that receives data in JSON format from a web-service which we are also in control of. The plan is to change out the backend database in a bout 18 months in favor of a different platform.

With that in mind, we want to be sure that that iOS app is going to be relatively easy to adapt to the new datasource, particularly as we may change the keys used in the associative array received from the server via JSON.

There are two goals:

  1. Create a single location for each PHP request where the keys can be modified if needed. This would avoid digging through code to find things like job["jobNumber"].

  2. Clean up our existing code to also eliminate references like job["jobNumber"].

We are both very new to Swift with no Objective-C experience, but I am was thinking a Struct or Class would be appropriate to create references like job.jobNumber.

Should a dictionary be converted into a class or struct? Sample code representing a reusable method of taking a Dictionary<String, String> as shown below and converting it to the recommended type would be extremely helpful.

Example Dictionary:

job = {
    "jobNumber" : "1234",
    "jobName" : "Awards Ceremony",
    "client" : "ACME Productions"
}

Desired result:

println("job name is \(job.name)")
// prints: job name is Awards Ceremony
like image 740
Michael Voccola Avatar asked Apr 10 '15 02:04

Michael Voccola


People also ask

Can a custom struct be used as a dictionary key Swift?

Any type that conforms to the Hashable protocol can be used as a dictionary's Key type, including all of Swift's basic types. You can use your own custom types as dictionary keys by making them conform to the Hashable protocol.

Which is faster struct or class in Swift?

So based on the above theory we can say that Struct is faster than Class because: To store class, Apple first finds memory in Heap, then maintain the extra field for RETAIN count.

How are dictionaries implemented in Swift?

Swift dictionary is made of two generic types: Key (which has to be Hashable ) and Value. An entry can be made by providing a key and its value. A value can be retrieved by providing a key which has been inserted before. A entry can be deleted by providing a key.


Video Answer


3 Answers

To access it like this you need to convert your dictionary to Struct as follow:

edit/update: Swift 3.x

struct Job: CustomStringConvertible {
    let number: Int
    let name, client: String
    init(dictionary: [String: Any]) {
        self.number = dictionary["jobNumber"] as? Int ?? 0
        self.name = dictionary["jobName"] as? String ?? ""
        self.client = dictionary["client"] as? String ?? ""
    }
    var description: String {
        return "Job#: " + String(number) + " - name: " + name + " - client: " + client
    }
}

let dict: [String: Any] = ["jobNumber": 1234,
                                     "jobName"  : "Awards Ceremony",
                                     "client"   : "ACME Productions"]

let job = Job(dictionary: dict)
print(job.number)       //  1234
print(job.name)         //  "Awards Ceremony"
print(job.client)       //  "ACME Productions"
print(job)              // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions"""

edit/update:

Swift 4 or later you can use JSON Codable protocol:

struct Job {
    let number: Int
    let name, client: String
}
extension Job: Codable {
    init(dictionary: [String: Any]) throws {
        self = try JSONDecoder().decode(Job.self, from: JSONSerialization.data(withJSONObject: dictionary))
    }
    private enum CodingKeys: String, CodingKey {
        case number = "jobNumber", name = "jobName", client
    }
}
extension Job: CustomStringConvertible {
    var description: String {
        return "Job#: " + String(number) + " - name: " + name + " - client: " + client
    }
}

let dict: [String: Any] = ["jobNumber": 1234,
                           "jobName"  : "Awards Ceremony",
                           "client"   : "ACME Productions"]
do {
    let job = try Job(dictionary: dict)
    print(job.number)       //  1234
    print(job.name)         //  "Awards Ceremony"
    print(job.client)       //  "ACME Productions"
    print(job)              //  "Job#: 1234 - name: Awards Ceremony - client: ACME Productions\n"
} catch {
    print(error)
}
like image 156
Leo Dabus Avatar answered Oct 17 '22 10:10

Leo Dabus


Definitely a job for a struct.
1. Structs are thread-safe and don't need to be managed by ARC.
2. Some studies have found them to be about 30,000x faster to work with than classes in general.
3. Structs also provide default initializers so your code will be cleaner.
4. In this case, you don't have to worry about inheritance/subclassing.
5. The Protocol Oriented Programming paradigm recommends using structs over classes if you're able.

struct Job {
    let number: Int
    let name: String
    let client: String
}

Initializer for free:

let newJob = Job(number: 2, name: "Honey", client: "Jeff")

Or you can create a custom initializer that takes the dictionary:

struct Job {
    let number: Int
    let name: String
    let client: String

    init(json: [String: Any]) {
        self.number = Int(dictionary["jobNumber"] as? String) ?? 0
        self.name = dictionary["jobName"] as? String ?? ""
        self.client = dictionary["client"] as? String ?? ""
    }
}

usage:

let newJob = Job(json: yourDictionary)
print(newJob.number) 

// outputs: 1234
like image 3
ScottyBlades Avatar answered Oct 17 '22 11:10

ScottyBlades


You can add an extension to Dictionary like this to get generic objects:

extension Dictionary where Key == String, Value: Any {

    func object<T: Decodable>() -> T? {
        if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
            return try? JSONDecoder().decode(T.self, from: data)
        } else {
            return nil
        }
    }
}

and use like this on any [String: Any] dictionaries:

let object: MyDecodableStruct? = dictionary.object()
like image 2
Behrad Kazemi Avatar answered Oct 17 '22 10:10

Behrad Kazemi