1

I have this working but it seems like a very manual process and I can't work out how to loop inside a loop (or if I should). Right now I am just testing this with 3 variables, but there will ultimately be about 100. Here's my playground. Is there a way to simplify this so I don't have to manually add each array name?

import Foundation

var json_data_url = "216.92.214.107/data_test.json"
var LRKSFOweekdayDep : [String] = [String]()
var LRKSFOweekendDep : [String] = [String]()
var SFOLRKweekdayDep : [String] = [String]()

let journeysURL:NSURL = NSURL(string: json_data_url)!
let data = NSData(contentsOfURL: journeysURL)!

do {
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
    print(json)

    if let dep_obj =  json as? NSDictionary {
        if let array_journey = dep_obj["journey"] as? NSArray{
            if let journies = array_journey[0] as? NSDictionary {
                if let array_dep = journies["LRKSFOweekdayDep"] as? NSDictionary{
                    if let dep = array_dep["dep"] as? NSArray {
                        for var i = 0; i < dep.count; ++i
                        {
                            let add = dep[i] as! String
                            LRKSFOweekdayDep.append(add)
                        }
                        print(LRKSFOweekdayDep)
                    }
                }
            }

            if let journies = array_journey[1] as? NSDictionary {
                if let array_dep = journies["LRKSFOweekendDep"] as? NSDictionary{
                    if let dep = array_dep["dep"] as? NSArray {
                        for var i = 0; i < dep.count; ++i
                        {
                            let add = dep[i] as! String
                            LRKSFOweekendDep.append(add)
                        }
                        print(LRKSFOweekendDep)
                    }
                }
            }

            if let journies = array_journey[2] as? NSDictionary {
                if let array_dep = journies["SFOLRKweekdayDep"] as? NSDictionary{
                    if let dep = array_dep["dep"] as? NSArray {
                        for var i = 0; i < dep.count; ++i
                        {
                            let add = dep[i] as! String
                            SFOLRKweekdayDep.append(add)
                        }
                        print(SFOLRKweekdayDep)
                    }
                }
            }
        }
    }
} catch {
    print("error serializing JSON: \(error)")
}
3
  • It might make it easier to help you if you explained what you want to happen; there's quite a lot of repetition in your code right now. Commented Dec 18, 2015 at 23:14
  • I will try. For each one of these (theres 100 of them) "LRKSFOweekendDep", there is an array like this ["16:00", "16:30", "17:00"]. I want to parse the JSON file and output each array in a swift file. Commented Dec 18, 2015 at 23:39
  • 1
    BTW, do not use NSData(contentsOfURL:), because that's synchronous. Use NSURLSession's dataTaskWithURL. Also, do not use data! because if data was nil for reasons outside of your control (e.g. the web server is down, internet is temporarily interrupted, etc.), it will crash. Commented Dec 19, 2015 at 2:00

2 Answers 2

1

You might want to look at using SwiftyJSON to make the parsing easier.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks.. I looked at SwiftyJSON.. but I could not see how it was going to be any different. At least for what I was trying to do. I will look again.
This doesn't answer the question. And link only answers are discouraged. This should be a comment, not an answer.
0

Right now, you have something like:

if let dep = array_dep["dep"] as? NSArray {
    for var i = 0; i < dep.count; ++i {
        let add = dep[i] as! String
        LRKSFOweekendDep.append(add)
    }
}

That can be simplified to:

LRKSFOweekendDep = array_dep["dep"] as? [String]

That assumes of course, that you define LRKSFOweekendDep to be optional. If it's not optional, you can do:

LRKSFOweekendDep = array_dep["dep"] as? [String] ?? []

But, it should be optional.


In a comment, you say that there are going to be 100 of these. Rather than having a variable for each, I would have thought that you'd rather keep an array of objects. For example, consider:

struct Journey {
    let name: String
    let departures: [String]
}

Then, to parse your JSON, you could iterate through the results:

let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])

var journeys = [Journey]()

if let results = json as? [String: AnyObject], let array = results["journey"] as? [[String: AnyObject]] {
    for dictionary in array {
        for (name, departures) in dictionary {
            if let departureDictionary = departures as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
                journeys.append(Journey(name: name, departures: departureList))
            }
        }
    }
}

Finally, I would advise against NSData(contentsOfURL:), because that's synchronous. Use NSURLSession's dataTaskWithURL, which is asynchronous. Also, if you use data! pattern, first check to make sure it's not nil. Otherwise, if data was nil for any reason outside of your control (e.g. the web server is down, internet is temporarily interrupted, etc.), the app will crash rather than handling it gracefully.

Putting that all together, you get something like:

func retrieveJourneys(completionHandler: ([Journey]?, NSError?) -> ()) {
    let task = NSURLSession.sharedSession().dataTaskWithURL(journeysURL) { data, response, error in
        guard error == nil && data != nil else {
            completionHandler(nil, error)
            return
        }

        var json: [String: AnyObject]?

        do {
            json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject]
        } catch let parseError as NSError {
            completionHandler(nil, parseError)
        }

        var journeys = [Journey]()

        if let array = json!["journey"] as? [[String: AnyObject]] {
            for dictionary in array {
                for (name, departures) in dictionary {
                    if let departureDictionary = departures as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
                        journeys.append(Journey(name: name, departures: departureList))
                    }
                }
            }
        }
        completionHandler(journeys, nil)
    }
    task.resume()
}

And then you'd use it like so:

var journeys: [Journey]?

override func viewDidLoad() {
    super.viewDidLoad()

    retrieveJourneys { journeys, error in
        guard error == nil && journeys != nil else {   // make sure it didn't have network problem
            print(error)
            return
        }

        dispatch_async(dispatch_get_main_queue()) {    // now update model on main queue
            self.journeys = journeys

            // and, for giggles and grins, this is how you might grab the first one and examine it:

            let someJourney = self.journeys![0]
            print(someJourney.name)
            print(someJourney.departures)
        }
    }
}

Now, the above assumes that you wanted an ordered list of journeys, sorted by the order you received them.

On the other hand, if you didn't care about the order, but wanted an efficient way to retrieve the departures associated with a given key, you might use a dictionary, instead:

func retrieveDepartures(completionHandler: ([String: [String]]?, NSError?) -> ()) {
    let task = NSURLSession.sharedSession().dataTaskWithURL(journeysURL) { data, response, error in
        guard error == nil && data != nil else {
            completionHandler(nil, error)
            return
        }

        var json: [String: AnyObject]?

        do {
            json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject]
        } catch let parseError as NSError {
            completionHandler(nil, parseError)
        }

        var departures = [String: [String]]()

        if let array = json!["journey"] as? [[String: AnyObject]] {
            for dictionary in array {
                for (name, departureObject) in dictionary {
                    if let departureDictionary = departureObject as? [String: [AnyObject]], let departureList = departureDictionary["dep"] as? [String] {
                        departures[name] = departureList
                    }
                }
            }
        }
        completionHandler(departures, nil)
    }
    task.resume()
}

And then:

var departures: [String: [String]]?

override func viewDidLoad() {
    super.viewDidLoad()

    retrieveDepartures { departures, error in
        guard error == nil && departures != nil else {
            print(error)
            return
        }

        dispatch_async(dispatch_get_main_queue()) {
            self.departures = departures

            // and, for giggles and grins, this is how you might grab a list of departures given a particular key

            let departureTimes = self.departures!["LRKSFOweekdayDep"]
            print(departureTimes)
        }
    }
}

8 Comments

ooh.. that looks promising.. I will work with this tonight and see where I get.. Thanks
So, I can get this to work, sort of. Two issues. The NSURLSession's dataTaskWithURL you have suggested gives me an error _NSCFLocalDataTask: 0x7fc5e052b3f0>{ taskIdentifier: 1 } Second. If I use NSData(contentsOfURL:) i can get a result of "[Journey(name: "LRKSFOweekdayDep", departures: ["5:45", " etc... but how would I pick out each pair?
Re NSURLSession error, show us the full error. I don't see any error there. That looks like you just printed the task. Bottom line, you definitely want to use sessions, not NSData(contentsOfURL:). Re using that array of Journey objects, let's say you want the n'th one, then you'd do let journey = journeys[n] and then you can do print(journey.name) or print(journey.departures).
OK.. your'e right. That's not an error. I have that part working now. Thanks. I'm still not getting the next part though . slow learner sorry. What I want to do is create each result as a global variable.. so for example I have this var LRKSFOweekdayDep : [String] = [String]() in a variables file and I want to match up the results from the JSON parse to each of these..
My suggestion was an alternative to 100 different variables (that's how many you're talking about, right?). I was suggesting you use some collection type. I used an array because your JSON suggested you cared about the order (e.g. you might want to show them in a table view in the order they appeared in the JSON). If you don't care about order, you can use dictionary, for which you can easily retrieve the departures associated with LRKSFOweekdayDep, for example.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.