I am pretty new to Xcode and Swift. I am trying to display data (restaurants names) in alphabetical order in TableView. I have a JSON file that sorts each restaurant into th
I tried to solve your problem using some different approach. Here is the Sample I made.
Here is my controller code and tried to make it as similar to contact app as per the data provided by the API. I just took restaurant name in model as It will only be needed for sorting in alphabetical order. All the other details and explanation are mentioned in comments within the code.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var restaurantsTableView: UITableView!
//The main array for tableView
var dataArray = [(String,[Restaurant])]()
var indexTitles = [String]()
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
func getData() {
let url = URL(string: "http://barhoppersf.com/json/neighborhoods.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data else {return}
let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String : AnyObject]
guard let hoods = json?["hoods"] else { return }
guard let names = hoods["neighborhoodNames"] as? [String:[AnyObject]] else { return }
self.makeDataSource(names: names)
DispatchQueue.main.async {
self.restaurantsTableView.reloadData()
}
}.resume()
}
// The main logic for sorting and making the data like Contacts App tableView
func makeDataSource(names:[String:[AnyObject]]) {
//Temporary array to hold restaurants on different indexes
var dict = [String:[Restaurant]]()
//Character set taken to check whether the starting key is alphabet or any other character
let letters = NSCharacterSet.letters
for (_,value) in names {
//Iterating Restaurants
for resObj in value {
if let restaurantName = resObj["name"] as? String {
let restaurant = Restaurant(name: restaurantName)
var key = String(describing: restaurant.name.characters.first!)
key = isKeyCharacter(key: key, letters: letters) ? key : "#"
if let keyValue = dict[key] {
//Already value exists for that key
var filtered = keyValue
filtered.append(restaurant)
//Sorting of restaurant names alphabetically
filtered = filtered.sorted(by: {$0.0.name < $0.1.name})
dict[key] = filtered
} else {
let filtered = [restaurant]
dict[key] = filtered
}
}
}
}
//To sort the key header values
self.dataArray = Array(dict).sorted(by: { $0.0 < $1.0 })
//Logic to shift the # category to bottom
let temp = self.dataArray[0]
self.dataArray.removeFirst()
self.dataArray.append(temp)
//For setting index titles
self.indexTitles = Array(dict.keys.sorted(by: <))
//Making the index title # at the bottom
let tempIndex = self.indexTitles[0]
self.indexTitles.removeFirst()
self.indexTitles.append(tempIndex
}
}
//Function to check whether key is alphabet or not
func isKeyCharacter(key:String,letters:CharacterSet) -> Bool {
let range = key.rangeOfCharacter(from: letters)
if let _ = range {
//Your key is an alphabet
return true
}
return false
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray[section].1.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RestaurantsTableCell") as! RestaurantsTableCell
let restaurant = dataArray[indexPath.section].1[indexPath.row]
cell.restaurantNameLabel.text = restaurant.name
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Pass this model to detail VC
let restaurant = dataArray[indexPath.section].1[indexPath.row]
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
detailVC.restaurant = restaurant
self.navigationController?.pushViewController(detailVC, animated: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dataArray[section].0
}
//For index titles
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return self.indexTitles
}
}
The Restaurant class is now separated. Also modified the Sample in the link and created a new DetailViewController
class.
import Foundation
class Restaurant {
var name = ""
init(name:String) {
self.name = name
}
}
This code can be improved in the sort logic. Improvements are welcome.
Here is the Output:
Here is the detail controller: