23

I'm trying to find a way to get the value of a key in a SwiftyJSON dictionary and return a default string, if the key is not set. The following example works fine, but I'm not really satisfied. Do you know a more elegant way?

Example:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]
    
for (key: String, user: JSON) in users {      
    println(user.object.objectForKey("name") != nil
        ? user["name"].stringValue
        : "default")
}
Community
  • 1
  • 1
tomatentobi
  • 3,119
  • 3
  • 23
  • 29

6 Answers6

28

The latest version of SwiftyJSON has the exists() function.

NOTE: Their latest documentation does not reflect the code very well... The actual method is exists()

// Returns boolean

json["name"].exists()
ciauri
  • 513
  • 5
  • 13
9

It seems, SwiftyJSON sets the error property when subscript non-existing key.

So, this should works:

for (key: String, user: JSON) in users {
    let name = user["name"];
    println(name.error == nil ? name.stringValue : "default")
}

For example: w/ Version 6.1.1 (6A2006), SwiftyJSON github current master:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": NSNull()],
    ["id": 4, "name": "four"],
]

for (key: String, user: JSON) in users {
    let name = user["name"];
    name.isEmpty
    println(name.error == nil ? name.stringValue : "default")
}

prints:

one
default

four
rintaro
  • 51,423
  • 14
  • 131
  • 139
  • 1
    I also tried this way, but it directly crashes after `let name = user["name"];`. – tomatentobi Dec 17 '14 at 20:47
  • At least, it works in my environment, as added to the answer. Please provides more info, what error? – rintaro Dec 18 '14 at 05:19
  • Your version (http://imgur.com/ZDiyRds) works until the second user. Then it can't find the getter (http://imgur.com/GgjiEUA). Do you use a different master? https://github.com/SwiftyJSON/SwiftyJSON/blob/master/Source/SwiftyJSON.swift – tomatentobi Dec 18 '14 at 20:48
7

If you need to check if a SwiftyJSON dictionary has a key, you can compare it with JSON.null as follows:

user["name"] == JSON.null // true if the key does not exist

Using that method, your code could look like this:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for (key, user) in users {
    print(user["name"] != JSON.null ? user["name"].stringValue : "default")
}

If you just want to provide a default value, then you can use the Nil Coalescing Operator in Swift (??) like this:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for (key, user) in users {
    print(user["name"].string ?? "default")
}

SwiftJSON provides the string method, which returns an optional value (String?), contrary to stringValue which always returns a String.

Eneko Alonso
  • 18,884
  • 9
  • 62
  • 84
1

Objects of type JSON have a calculated property called null. This returns NSNull if the element requested does not exist, nil if it does. You can therefore test for the existence of a specific object by extracting it from its parent JSON and testing to see if the null property exists. I personally handle it like this:

let object = json["key"]
if object.null == nil {
    // Do something with 'object'
} else {
    // Key does not exist - handle the error
}

In your specific case, you could use this:

for (key: String, user: JSON) in users {
    let name = user.object.objectForKey("name")
    println(name.null == nil ? name.stringValue : "default")
}

...but since we know we're looking for a string we can cut that down even further. All you actually need to do is drop the 'Value' part of stringValue, since string will return nil if it cannot generate a string from its content. Also, check out the nil coalescence operator ??. It's designed to provide a default for optional variables, so try this:

for (key: String, user: JSON) in users {
    println(user["name"].string ?? "default")
}

Nice and compact!

Ash
  • 9,064
  • 3
  • 48
  • 59
  • Oops, just realized my answer is almost the same as the last part you wrote. Well, for easier access, I guess I'll leave the answer – Mazyod Sep 10 '15 at 10:30
0

As far as I can tell, this works:

let users: JSON = [
    ["id": 1, "name": "one"],
    ["id": 2],
    ["id": 3, "name": "three"]
]

for jsonDict in users.arrayValue {      
    println(jsonDict["name"].string ?? "default")
}

To explain, you want to cast the JSON object to the types you expect to get. If you want the type to always succeed (not optional), use xxxValue. Hence, we cast the root object using arrayValue.

arrayValue returns [JSON], which we traverse in the loop. We don't need to case the jsonDict: JSON to object, as SwiftyJSON is smart enough to assume it is a dict from the subscript operators.

Finally, trying to fetch a key from the dict, then checking if the key can be retrieved as a string using .string, which is an optional. Use null coalescing to assign a default value.

Please see the SwiftJSON doc for string and stringValue

Mazyod
  • 22,319
  • 10
  • 92
  • 157
-1

Note: This answer is no longer valid

Seems like the perfect use of if let syntax. Because accessing the subscript will return an optional, bind it to a variable in an if let block. This will tell you wether or not the key was in the JSON.

if let name = user["name"] {    // returns optional
    // user["name"] was non-nil, is now stored in ``name""
    // do something with ``name""
} 
else {
    // user did not have key "name"
    // here would be the place to return "default"
    // or do any other error correction if the key did not exist
}
Brian Tracy
  • 6,801
  • 2
  • 33
  • 48
  • No, this is invalid: http://i.imgur.com/ST0MgLX.png. Making a cast as JSON crashes too: http://i.imgur.com/wpwlI6H.png – tomatentobi Dec 18 '14 at 20:33