wigging wigging - 5 months ago 10
JSON Question

Using guard in Swift struct does not initialize all properties

For Example A I have the following code for a

struct
in Swift:

struct Location {
let name: String
let tzs: String
let lat: String
let lon: String

init(jsonDict: [String: AnyObject]) {

guard let name = jsonDict["name"] as? String else {
self.name = "none"
return
}

guard let tzs = jsonDict["tzs"] as? String else {
self.tzs = "none"
return
}

guard let lat = jsonDict["lat"] as? String else {
self.lat = "none"
return
}

guard let lon = jsonDict["lon"] as? String else {
self.lon = "none"
return
}

self.name = name
self.tzs = tzs
self.lat = lat
self.lon = lon
}
}


The properties are initialized from a dictionary obtained from parsing JSON data. Unfortunately, the code provides a warning about "Return from initializer without initializing all stored properties".

For Example B I tried optional chaining with the guard statements as seen below:

struct Location {
let name: String
let tzs: String
let lat: String
let lon: String

init(jsonDict: [String: AnyObject]) {

guard
let name = jsonDict["name"] as? String,
let tzs = jsonDict["tzs"] as? String,
let lat = jsonDict["lat"] as? String,
let lon = jsonDict["lon"] as? String
else {
self.name = "no"
self.tzs = "no"
self.lat = "no"
self.lon = "no"
return
}

self.name = name
self.tzs = tzs
self.lat = lat
self.lon = lon
}
}


The optional chaining approach in Example B works but it requires all the checks in the guard statement to be true. If one of the variables is not in the JSON data then the whole statement will be false. For example, if
tzs
is not in the
jsonDict
then all the properties will be "no".

I'm am running all of this code in a Playground (if that matters).

How can I get Example A to initialize the
struct
if one or more properties are not in the JSON dictionary?

Answer

Use the nil coalescing operator (??):

init(jsonDict: [String: AnyObject]) {
    name = jsonDict["name"] as? String ?? "none"
    tzs = jsonDict["tzs"] as? String ?? "none"
    lat = jsonDict["lat"] as? String ?? "none"
    lon = jsonDict["lon"] as? String ?? "none"
}

The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.

Or, of course, you could make the properties optional and substitute the "none" string if the optional is nil when you go to access them somewhere else (ex. when you show this location to the user).