// // ForayTableViewController.swift // foray // // Created by Nicholas Tay on 14/3/2022. // import UIKit enum ItemType: String, Decodable { case item case quest } struct MyItem: Decodable { var type: ItemType var releaseDate: Date var id: String var name: String } struct YearSection { var year: Date var items: [MyItem] } class ForayTableViewCell: UITableViewCell { @IBOutlet weak var cellItemName: UILabel! @IBOutlet weak var cellItemSubtitle: UILabel! @IBOutlet weak var cellItemImage: UIImageView! } // copied from sample project private func parseDate(_ str : String) -> Date { let dateFormat = DateFormatter() dateFormat.dateFormat = "yyyy-MM-dd" return dateFormat.date(from: str)! } private func firstDayOfYear(date: Date) -> Date { let calendar = Calendar.current let components = calendar.dateComponents([.year], from: date) return calendar.date(from: components)! } class ForayTableViewController: UITableViewController { // MARK: - Static data TEMP var items = [MyItem]() var sections = [YearSection]() // MARK: - On load override func viewDidLoad() { super.viewDidLoad() // Not sure if this is the right way to go about this... let alert = UIAlertController(title: nil, message: "Grabbing data...", preferredStyle: .alert) let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50)) loadingIndicator.hidesWhenStopped = true loadingIndicator.style = UIActivityIndicatorView.Style.medium loadingIndicator.startAnimating(); alert.view.addSubview(loadingIndicator) present(alert, animated: true, completion: nil) loadApiData(onComplete: { (apiItems) in self.items = apiItems let groups = Dictionary(grouping: self.items) { (item) in return firstDayOfYear(date: item.releaseDate) } self.sections = groups.map { (key, values) in return YearSection(year: key, items: values) } // Sort the sections from oldest year to newest self.sections.sort { (lhs, rhs) in lhs.year < rhs.year } self.tableView.reloadData() }) dismiss(animated: false, completion: nil) } func loadApiData(onComplete: @escaping ([MyItem]) -> ()) { // return [ // MyItem(type: .item, releaseDate: parseDate("2006-05-26"), id: "mh", name: "Miners Helmet"), // MyItem(type: .item, releaseDate: parseDate("2010-05-01"), id: "it", name: "Inner Tube"), // MyItem(type: .item, releaseDate: parseDate("2009-04-24"), id: "tbg", name: "Toboggan"), // MyItem(type: .item, releaseDate: parseDate("2006-03-29"), id: "spy", name: "Spy Phone"), // MyItem(type: .item, releaseDate: parseDate("2008-11-18"), id: "bnb", name: "Black Ninja Belt"), // MyItem(type: .quest, releaseDate: parseDate("2006-05-23"), id: "cmp", name: "Case of the Missing Puffles"), // MyItem(type: .quest, releaseDate: parseDate("2009-11-16"), id: "gsm", name: "G's Secret Mission"), // MyItem(type: .quest, releaseDate: parseDate("2009-04-18"), id: "cmc", name: "Case of the Missing Coins"), // ] var request = URLRequest(url: URL(string: "https://.../dummy.json")!) let authData = ("..:..").data(using: .utf8)!.base64EncodedString() request.addValue("Basic \(authData)", forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request, completionHandler: { data, response, error -> Void in print("finished getting data") print(response!) let jsonDecoder = JSONDecoder() jsonDecoder.dateDecodingStrategy = .custom({ (decoder) -> Date in let container = try decoder.singleValueContainer() let dateStr = try container.decode(String.self) return parseDate(dateStr) }) let items = try! jsonDecoder.decode([MyItem].self, from: data!) print("json decoded") // Passing back to UI, need to do it on the main thread (I think due to async?) DispatchQueue.main.async { onComplete(items) } }).resume() } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { // Returns number of sections for table return self.sections.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // Returns number of rows for table's section return self.sections[section].items.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = self.sections[indexPath.section].items[indexPath.row] let cell: ForayTableViewCell switch item.type { case .item: cell = tableView.dequeueReusableCell(withIdentifier: "ForayCell", for: indexPath) as! ForayTableViewCell cell.cellItemImage?.image = UIImage(named: item.id) case .quest: cell = tableView.dequeueReusableCell(withIdentifier: "ForayQuestCell", for: indexPath) as! ForayTableViewCell } cell.cellItemName?.text = item.name cell.cellItemSubtitle?.text = "ID: " + item.id return cell } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let section = self.sections[section] let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy" return "Released in " + dateFormatter.string(from: section.year) } // MARK: - Navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { let indexPath = tableView.indexPathForSelectedRow! let item = self.sections[indexPath.section].items[indexPath.row] let dvc = segue.destination as! ForayDetailViewController dvc.selectedItem = item } }