-3

I am new to Xcode and Swift. I need help displaying a data from JSON file into TableView with sections and rows. I need to display different restaurants in each neighborhood. I think I need to make changes in my JSON file but I can't figure it out. I really will appreciate any help. Cheers.

This is my JSON file:

{
"hoods": {

"neighborhoodNames": {
   "marina":[

{
  "name": "MARINA-1",
  "dob": "December 18, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
  "name": "MARINA-2",
  "description": "Tom Cruise, is an American film actor and producer. He has been nominated for three Academy Awards and has won three Golden Globe Awards. He started his career at age 19 in the 1981 film Endless Love.",
  "dob": "July 3, 1962",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
  "name": "MARINA-3",
  "description": "John Christopher 'Johnny' Depp II is an American actor, film producer, and musician. He has won the Golden Globe Award and Screen Actors Guild award for Best Actor.",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
}
],
   "MISSION":[

{
  "name": "MISSION-1",
  "dob": "December 18, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
  "name": "MISSION-2",  
  "dob": "July 3, 1962",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
},
{
  "name": "MISSION-3",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
},
 {
  "name": "MISSION-4",
  "dob": "June 9, 1963",
  "image": "http://microblogging.wingnity.com/JSONParsingTutorial/johnny.jpg"
}
]}
}
}

This is a link to the JSON file: http://barhoppersf.com/json/hoods.json

This my Xcode:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

  let urlString = "http://barhoppersf.com/json/hoods.json"



@IBOutlet weak var tableView: UITableView!

var nameArray:[[String]] = []
var dobArray:[[String]] = []
var imgURLArray:[[String]] = []
var neighborhoodNames = [String]()


override func viewDidLoad() {
    super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self


    self.downloadJsonWithURL()

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}




func downloadJsonWithURL() {

    let url = NSURL(string: urlString)

    URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in


        if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {


            if let actorArray = jsonObj!.value(forKey: "hoods") as? NSArray {

                for actor in actorArray{

                    if let actorDict = actor as? NSDictionary {


                        if let name = actorDict.value(forKey: "neighborhoodNames") {
                            self.neighborhoodNames.append(name as! String)
                        }
                        if let name = actorDict.value(forKey: "name") {
                            self.nameArray.append([name as! String])
                        }
                        if let name = actorDict.value(forKey: "dob") {
                            self.dobArray.append([name as! String])
                        }
                        if let name = actorDict.value(forKey: "image") {
                            self.imgURLArray.append([name as! String])
                        }

                    }
                }
            }

//                self.nameArray = self.nameArray.sorted()


            OperationQueue.main.addOperation({
                self.tableView.reloadData()
            })
        }
    }).resume()
}




 func downloadJsonWithTask() {

    let url = NSURL(string: urlString)

    var downloadTask = URLRequest(url: (url as URL?)!, cachePolicy:     URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 15)

    downloadTask.httpMethod = "GET"

    URLSession.shared.dataTask(with: downloadTask, completionHandler: {(data,   response, error) -> Void in

         let jsonData = try? JSONSerialization.jsonObject(with: data!, options:  .allowFragments)

        print(jsonData as Any)

    }).resume()
}


   // MARK: - changing the color, background and position of the header
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)  -> UIView? {
    let headerView = UIView()
    headerView.backgroundColor = UIColor.self.init(red: 254/255, green:  170/255, blue: 25/255, alpha: 1.0)


    let headerLabel = UILabel(frame: CGRect(x: 8, y: 5, width: tableView.bounds.size.width, height: tableView.bounds.size.height))

    headerLabel.font = UIFont(name: "Trebuchet MS", size: 15)
    headerLabel.textColor = UIColor.darkGray
    headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
    headerLabel.sizeToFit()
    headerView.addSubview(headerLabel)

    return headerView
}


    // MARK: - changing the size of the header cell
    //      func tableView(_ tableView: UITableView, heightForHeaderInSection  section: Int) -> CGFloat {
    //        return 40
    //    }


       func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

      return (nameArray[section].count)

}



     func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

    return neighborhoodNames[section]
}

     func numberOfSections(in tableView: UITableView) -> Int {
    return neighborhoodNames.count
}


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell

      //          cell.nameLabel.text = nameArray[indexPath.row]
         cell.nameLabel?.text = nameArray[indexPath.section][indexPath.row]


    return cell
}

  // set up A_Z index
   //    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    //        return indexName
    //    }

       // call correct section when index is tapped
       func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {

    guard let index = indexName.index(of: title) else {
        return -1
    }

    return index

}

      ///for showing next detailed screen with the downloaded info
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

     let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
    vc.imageString = imgURLArray[indexPath.section][indexPath.row]
    vc.nameString = nameArray[indexPath.section][indexPath.row]
    vc.dobString = dobArray[indexPath.section][indexPath.row]

    self.navigationController?.pushViewController(vc, animated: true)
}
}

Thanks a lot in advance!!! Dian.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Dian
  • 115
  • 3
  • 12

1 Answers1

0

Your JSON above and the JSON from that URL are different, so what I have here can work for both.

import UIKit

struct Actor {

    var children: String

    var country: String

    var description: String

    var dob: String

    var height: String

    var image: String

    var name: String

    var spouse: String

    init?(dict:Dictionary<String,String>) {

        guard

            let children = dict["children"],

            let country = dict["country"],

            let description = dict["description"],

            let dob = dict["dob"],

            let height = dict["height"],

            let image = dict["image"],

            let name = dict["name"],

            let spouse = dict["spouse"]

        else {

            return nil
        }

        self.children =  children

        self.country =  country

        self.description =  description

        self.dob =  dob

        self.height =  height

        self.image =  image

        self.name =  name

        self.spouse =  spouse
    }
}

struct Restaurant {

    var name: String

    var dob: String

    var image: String

    init?(dict:Dictionary<String,String>) {

        guard

            let name = dict["name"],

            let dob = dict["dob"],

            let image = dict["image"]

        else {

            return nil
        }

        self.name = name

        self.dob = dob

        self.image = image
    }
 }

struct NeighborhoodActors {

    var name: String

    var actors: Array<Actor>

    init(name:String, data:Array<Dictionary<String,String>>) {

        self.name = name

        self.actors = Array<Actor>()

        for dict in data {

            if let actor = Actor(dict: dict) {

                self.actors.append(actor)
            }
        }
    }
}

struct NeighborhoodRestaurants {

    var name: String

    var restaurants: Array<Restaurant>

    init(name:String, data:Array<Dictionary<String,String>>) {

        self.name = name

        self.restaurants = Array<Restaurant>()

        for dict in data {

            if let restaurant = Restaurant(dict: dict) {

                self.restaurants.append(restaurant)
            }
        }
    }
 }

class ViewController: UITableViewController {

    let urlString = "http://barhoppersf.com/json/hoods.json"

    var tableData = Array<NeighborhoodRestaurants>()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.downloadJsonWithURL() // This loads tableview with data from url

        load(file: "document") // This loads tableview with the json in your question, which I put in a json file to test
    }

    func load(file:String) {

        guard let path = Bundle.main.path(forResource: file, ofType: "json") else { return }

        guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { return }

        guard let json = try? JSONSerialization.jsonObject(with: data) else { return }

        guard let dict = json as? Dictionary<String,Dictionary<String,Dictionary<String,Array<Dictionary<String,String>>>>> else { return }

        guard let hoods = dict["hoods"] else { return }

        guard let names = hoods["neighborhoodNames"] else { return }

        for (key, value) in names {

            let neighborhood = NeighborhoodRestaurants(name: key, data: value)

            self.tableData.append(neighborhood)
        }

        self.tableData.sort { $0.name > $1.name } // This will arrange the restaurants alphabetically

        self.tableView.reloadData()
    }

    func downloadJsonWithURL() {

        let url = NSURL(string: urlString)

        URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in

            if let error = error {

                print(error.localizedDescription)

                return
            }

            if let data = data {

                guard let json = try? JSONSerialization.jsonObject(with: data) else { return }

                guard let dict = json as? Dictionary<String,Dictionary<String,Dictionary<String,Array<Dictionary<String,String>>>>> else { return }

                guard let hoods = dict["hoods"] else { return }

                guard let names = hoods["neighborhoodNames"] else { return }

                for (key, value) in names {

                    let neighborhood = NeighborhoodActors(name: key, data: value)

                    // self.tableData.append(neighborhood)
                }

                DispatchQueue.main.async {

                    self.tableView.reloadData()
                }
            }

        }).resume()
    }

    // MARK: - changing the color, background and position of the header
    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)  -> UIView? {

        let headerView = UIView()

        headerView.backgroundColor = UIColor.self.init(red: 254/255, green:  170/255, blue: 25/255, alpha: 1.0)

        let headerLabel = UILabel(frame: CGRect(x: 8, y: 5, width: tableView.bounds.size.width, height: tableView.bounds.size.height))

        headerLabel.font = UIFont(name: "Trebuchet MS", size: 15)

        headerLabel.textColor = UIColor.darkGray

        headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)

        headerLabel.sizeToFit()

        headerView.addSubview(headerLabel)

        return headerView
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return self.tableData[section].restaurants.count
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        return self.tableData[section].name
    }

    override func numberOfSections(in tableView: UITableView) -> Int {

        return self.tableData.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        cell.textLabel?.text = self.tableData[indexPath.section].restaurants[indexPath.row].name

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController

        vc.restaurant = self.tableData[indexPath.section].restaurants[indexPath.row]

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

class DetailViewController: UIViewController {

    @IBOutlet var imageView: UIImageView!

    @IBOutlet weak var nameLabel: UILabel!

    @IBOutlet weak var dobLabel: UILabel!

    var restaurant: Restaurant!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.nameLabel.text = self.restaurant.name

        self.dobLabel.text = self.restaurant.dob

        if let url = URL(string: self.restaurant.image) {

            let task = URLSession.shared.dataTask(with: url) { data, resonse, error in

                if let error = error {

                    print(error.localizedDescription)

                    return
                }

                if let data = data {

                    let image = UIImage(data: data)

                    DispatchQueue.main.async {

                        self.imageView.image = image
                    }
                }
            }

            task.resume()
        }
    }
}

This is how to open a URL, open apple maps, and ring a phone number.

func telephone(phoneNumber:String) {

    let application = UIApplication.shared

    if application.openURL(URL(string: "tel://\(phoneNumber)")!) {

        print("Ringing")

    } else {

        print("Something has gone wrong.")
    }
}

func directions(address:String) {

    let geocoder = CLGeocoder()

    geocoder.geocodeAddressString(address) { (placemarks, error) in

        if let description = error?.localizedDescription {

            print(description)

            return
        }

        if let placemark = placemarks?.first {

            let pin = MKPlacemark(placemark: placemark)

            let item = MKMapItem(placemark: pin)

            let region = MKCoordinateRegionMakeWithDistance(pin.coordinate, 1000, 1000)

            let options: Dictionary<String,Any> = {

                var dict = Dictionary<String,Any>()

                dict[MKLaunchOptionsMapCenterKey] = NSValue(mkCoordinate: region.center)

                dict[MKLaunchOptionsMapSpanKey] = NSValue(mkCoordinateSpan: region.span)

                return dict
            }()

            item.openInMaps(launchOptions: options)

            return
        }

        print("An unknown error has occured.")
    }
}

func website(url:String) {

    if let url = URL(string: url) {

        let application = UIApplication.shared

        application.openURL(url)

        return
    }

    print("The URL is invalid.")
}
Tristan Beaton
  • 1,742
  • 2
  • 14
  • 25
  • Tristan thanks for your help and answer but it's not working here. Maybe I am doing something wrong. I got an error: 'Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UITableViewController loadView] loaded the "BYZ-38-t0r-view-8bC-Xf-vdC" nib but didn't get a UITableView.' Any idea? – Dian May 12 '17 at 10:03
  • In your storyboard, is it a UIViewController or UITableViewController? – Tristan Beaton May 12 '17 at 10:05
  • It is ViewController – Dian May 12 '17 at 10:08
  • If you add a UITableViewController to your storyboard and set the class the one I have made above it should work. If you need to use a UIViewController, you'll need to add a UITableView outlet and set the UITableViewDelegate and UITableViewDatasource. Also, Change the super class from UITableViewController to UIViewController, and remove override from all the tableview methods. – Tristan Beaton May 12 '17 at 10:12
  • I got it work. Thank you so much. Now I have to get the information display when I click on the cells (Marina-1, Marina-2...). I have it set up with TableViewCell and TableViewController. – Dian May 12 '17 at 10:28
  • func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController vc.imageString = imgURLArray[indexPath.section][indexPath.row] vc.nameString = nameArray[indexPath.section][indexPath.row] vc.dobString = dobArray[indexPath.section][indexPath.row] self.navigationController?.pushViewController(vc, animated: true) } – Dian May 12 '17 at 10:29
  • Tristan, the data is only loaded from the local file. I can't load the url.Any ideas? – Dian May 12 '17 at 11:08
  • Tristan, I got the url working. Thanks again man! I appreciate it. I just need to figure how display all the info from the url json inside the DetailViewController I have. I am using: func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController self.navigationController?.pushViewController(vc, animated: true) } THANKS in advance. – Dian May 12 '17 at 21:55
  • I've added some code above to deal with the cell selection. – Tristan Beaton May 12 '17 at 22:26
  • Tristan. Sorry for all the questions. I can't display the info I like in the DetailViewController. I am trying to show the name of the restaurant (ex: Marina-1), date of birth (dob) from the json as well as the image. Right now I have this in my DetailViewController: – Dian May 13 '17 at 07:18
  • ` @IBOutlet var imageView: UIImageView! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var dobLabel: UILabel! var nameString:String! var dobString:String! var imageString:String! override func viewDidLoad() { super.viewDidLoad() print(restaurant) self.updateUI() } – Dian May 13 '17 at 07:22
  • the rest.....func updateUI() { self.nameLabel.text = nameString self.dobLabel.text = dobString let imgURL = URL(string:imageString) let data = NSData(contentsOf: (imgURL)!) self.imageView.image = UIImage(data: data! as Data) } Hope you can help!!! – Dian May 13 '17 at 07:22
  • Tristan, maybe I can email you the current project. maybe will be easier. Thanks so much. Really really appreciate your help. – Dian May 13 '17 at 07:25
  • I have modified the DetailViewController above to work with your updateUI function. – Tristan Beaton May 13 '17 at 09:58
  • Tristan, Thank you so much for your help. Everything works perfectly. I really appreciate your help. Thank you! Thank you! – Dian May 13 '17 at 18:05
  • Tristan, Sorry to bug you but for some reason does't display the names of the hoods from the JSON file in the right order. Marina is first in the file but is getting displayed second. Maybe I need to sort them alphabetically which I actually need. Any idea?? Thanks man. – Dian May 13 '17 at 21:12
  • Do you want them in the order that they are in the JSON file? – Tristan Beaton May 13 '17 at 21:14
  • they will alphabetically eventually. so I can list them alphabetically in the JSON file or if there is a function to sort them alphabetically will work. Thanks man. – Dian May 13 '17 at 21:19
  • There is a sorting function to put them alphabetically. I am just testing it. – Tristan Beaton May 13 '17 at 21:20
  • This code here will sort them alphabetically `self.tableData.sort { $0.name > $1.name }`. I have also added it to the answer above. It is before `tableView.reloadData()` in the `loadFile()` function. – Tristan Beaton May 13 '17 at 21:25
  • It worked like a charm. Thanks again for your help. I am really new to this so any help is appreciate it. Cheers from San Francisco!! – Dian May 13 '17 at 21:30
  • do you think I can display Url address into Safari from the code you helped me with? Right now it displays label but I need to make it a button and parse the Json so I can click on each url that comes from the Json. – Dian May 23 '17 at 08:19
  • I don't really understand your question. Do you want the button to open a URL in safari? – Tristan Beaton May 23 '17 at 10:02
  • Sorry if I wasn't clear. Yes. I want the button to open a URL. What I m doing is displaying restaurants info into the detail view controler from the json. Each restaurant displays the name., cuisine..I have to make the website, location (address) and telephone number of active ( clickable). Right now tI have labels that just displays the name of the website but it is not clickable. Hope I make sense. Thanks buddy. – Dian May 23 '17 at 16:35
  • Use this in DidLoad: let tap = UITapGestureRecognizer(target: self, action: #selector(DetailViewController.tapFunction)) websiteLabel.isUserInteractionEnabled = true websiteLabel.addGestureRecognizer(tap) – Dian May 23 '17 at 18:47
  • .......... func tapFunction(sender:UITapGestureRecognizer) { UIApplication.shared.open(URL(string: self.restaurant.website)!, options: [:], completionHandler: nil) } – Dian May 23 '17 at 18:47
  • I just need to figure out how to call a number on tapping the number and open apple map on tapping the address. Any ideas? – Dian May 23 '17 at 19:41
  • You can use the `UIApplication.shared.open()` for both phone numbers and maps. [This is for phone numbers](https://stackoverflow.com/questions/27259824/calling-a-phone-number-in-swift). [This for maps](https://stackoverflow.com/questions/21746373/open-maps-with-specific-address-ios-7) – Tristan Beaton May 23 '17 at 19:44
  • I am trying this function but not luck: func phone(phoneLabel: String) { if let url = URL(string: self.restaurant.phone) { if #available(iOS 10, *) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { UIApplication.shared.openURL(url as URL) } } } – Dian May 23 '17 at 20:29
  • I wonder if I have to put phoneLabel.isUserInteractionEnabled = true – Dian May 23 '17 at 20:37
  • also the problem is that the link you sent they are using a button and I am using a label. Thanks again for your help. Really appreciated. – Dian May 23 '17 at 20:57
  • Do you have to use labels? Because I would swap them for buttons. If you are going to user labels, use the tap gesture action to call the functions I have added to my answer. – Tristan Beaton May 23 '17 at 23:54
  • I will check it later. Do you think this will work with the current labels I have? Thanks a lot buddy. – Dian May 24 '17 at 01:53
  • Can you add a screenshot of what your view controller looks like? I will be able to tell if your labels are going to work from that. – Tristan Beaton May 24 '17 at 01:55
  • I made the phone works. I used a button. Now I just need to figure out how to open the address in maps. Thanks. I am using this statement: self.addressButton.setTitle(self.restaurant.address,for: .normal) self.phoneButton.setTitle(self.restaurant.phone,for: .normal) – Dian May 24 '17 at 07:38
  • I just used and worked: @IBAction func addressButton(_ sender: Any) { let baseUrl: String = "http://maps.apple.com/?q=" let encodedName = restaurant.address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! let finalUrl = baseUrl + encodedName if let url = URL(string: finalUrl) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } – Dian May 24 '17 at 07:57
  • Thanks again for your input. Really appreciate it. – Dian May 25 '17 at 22:20
  • Hope you are doing well. see if you can help me out with this question:Thanks buddy. https://stackoverflow.com/questions/44877074/display-data-from-json-in-alphabetical-sections-in-table-view-in-swift – Dian Jul 03 '17 at 02:45