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:
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"]
.
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
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.
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.
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.
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)
}
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
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With