Converting JSON from AlamoFire/SwiftyJSON to Dictionary in Swift/Xcode
Asked Answered
D

4

8

My head is going to explode :) - I've been trying to get a JSON String from my server to a Dictionary Value, and I can't get it to work.

I'm trying to get (from my Server - this is dynamic and I want my app to be able to pull new data from the server when needed):

{"1":"Location 1","2":"Location 2","3":"Location 3"}

To this Dictionary in Xcode using Swift:

  var labels = [

        1 : "Location 1",
        2 : "Location 2",
        3 : "Location 3"

    ]

This has got to be pretty straight forward, but for the life of me I can't figure it out...

Here's my Swift - I can get it to pull the information from the server, but I can't get it into a dictionary like I need

var postEndpoint: String = "http://www.myserver.net/app/campus.php"

    Alamofire.request(.GET, postEndpoint)
        .responseJSON { (request, response, data, error) in
            if let anError = error
            {
                println("error")
                println(error)
            }
            else if let data: AnyObject = data 
            {
                 let post = JSON(data)
                 println(post)
            }
    }

which results in:

{
  "1" : "Location 1",
  "2" : "Location 2",
  "3" : "Location 3"
}

The End Result that I'm using this for is an iBeacon implementation with the following code:

 let knownBeacons = beacons.filter{ $0.proximity != CLProximity.Unknown }
    if (knownBeacons.count > 0) {
        let closestBeacon = knownBeacons[0] as CLBeacon

        let locationID = post[closestBeacon.minor.integerValue]

        self.locationLabel.text = locationID
        self.view.backgroundColor = self.colors[closestBeacon.minor.integerValue]
    }

The error I'm getting is at self.locationLabel.text = locationID 'JSON' is not convertible to 'String', I do not get this error when I use the static var labels dictionary. Am I trying to get the data from the server incorrectly? What am I doing wrong??? I think the var labels having an undeclared Type allows Swift to figure out what it needs to, how do I do the same from the JSON part?

Distinguish answered 4/3, 2015 at 18:27 Comment(0)
C
11

Oh you were so close!

Your problem is that your post dictionary is a [String: String] and not an [Int: String] like you think it is. You have a few ways to fix it, but the easiest for now would be to just do the following:

let locationID = post[String(closestBeacon.minor.integerValue)]!

While this will certainly work, a better solution would be to convert your post into a [Int: String] typed dictionary like you expect in the responseJSON closure. Here's how this could work.

let json = JSON(data)
var post = [Int: String]()

for (key, object) in json {
    post[key.toInt()!] = object.stringValue
}

You would want to add some safety around what to do if the key or object were not able to be converted to an Int or String respectively, but I'll leave that to you.

Califate answered 5/3, 2015 at 16:25 Comment(3)
Thanks! How would I map it? Sorry, I'm totally new to swift.Distinguish
Hi @davidbii, I updated my answer to show how to convert json object into an [Int: String] dictionary.Califate
Is there a recursive way if my json is 'deeper'? With the result being in [String: AnyObject]Tracheo
H
7

If having a [String: String] is sufficient for anyone, he/she could try the following code:

let responseString = "{\"1\":\"Location 1\",\"2\":\"Location 2\",\"3\":\"Location 3\"}"

if let dataFromString = responseString.data(using: String.Encoding.utf8, allowLossyConversion: false) {

    let json = JSON(data: dataFromString)

    var labels = json.dictionaryObject! as! [String: String]

    print(labels)
}

The result is: ["2": "Location 2", "1": "Location 1", "3": "Location 3"].

Hat answered 3/10, 2016 at 8:35 Comment(0)
D
0

You need to check recursively for inner JSON as well Use below function to convert into Dictionary or Array

static func toDictionary(from: JSON) -> Any? {
    if from.type == .array {
        var array = [Any]()
        for _i in 0..<from.count {
            array.append(toDictionary(from: from[_i]) as Any)
        }
        return array
    } else if from.type == .dictionary {
        var dictionary = [String: Any]()
        for (key, value) in from {
            dictionary[key] = toDictionary(from: value)
        }
        return dictionary
    } else {
        return from.stringValue
    }
}
Dope answered 4/3, 2015 at 18:27 Comment(1)
realise you've got a lot of code there, but is this answering the OP question - or just a guide for good practice. Please update your answer - to refer to the solution (and then include the good practice), OR convert into an answer.Tailored
D
0

I solved this by working in reverse. I changed the call. Instead of getting the Dictionary of Values from the Server, I just query the Server with the Single Variable that I already had from the variable

closestBeacon.minor.integerValue

And then get the string that I needed from the server and that solved my problem. Still has the same number of calls to the server, so no additional overhead was added. Sometimes you just have to think outside the box that you put yourself into.

If anybody can solve this the other direction, I'm still eager to hear how it could work.

Distinguish answered 4/3, 2015 at 23:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.