1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
//
// ForayTableViewController.swift
// foray
//
// Created by Nicholas Tay on 14/3/2022.
//
import UIKit
enum ItemType: String, Decodable {
case item
case quest
}
struct PenguinItem: Decodable {
var type: ItemType
var releaseDate: Date
var id: String
var name: String
}
struct YearSection {
var year: Date
var items: [PenguinItem]
}
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 sections = [YearSection]()
// MARK: - On load
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Foray"
tableView.rowHeight = UITableView.automaticDimension
// Register our custom cell
tableView.register(ForayNewTableViewCell.self, forCellReuseIdentifier: "ForayNewTableViewCell")
// 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)
reloadApiData()
dismiss(animated: false, completion: nil)
// Not 100% sure what this does (the for: bit)
self.refreshControl?.addTarget(self, action: #selector(doRefresh), for: UIControl.Event.valueChanged)
}
@objc func doRefresh(sender: AnyObject) {
reloadApiData()
}
func reloadApiData() {
loadApiData(onComplete: { (apiItems) in
var items = apiItems
items.sort { (lhs, rhs) in lhs.releaseDate < rhs.releaseDate }
let groups = Dictionary(grouping: apiItems) { (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()
self.refreshControl?.endRefreshing()
})
}
func loadApiData(onComplete: @escaping ([PenguinItem]) -> ()) {
var request = URLRequest(url: URL(string: "https://users.windblume.net/~nick/upload/dummy.json")!)
request.cachePolicy = .reloadRevalidatingCacheData // Needed otherwise default caching policy seems not to check properly
// Basic auth if required
//let authData = ("ext:PASSWORD").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([PenguinItem].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 type: String
let icon: UIImage
switch item.type {
case .item:
type = "Item"
icon = UIImage(named: item.id)!
case .quest:
type = "Quest"
icon = UIImage(named: "spy")!
}
let cell: ForayNewTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ForayNewTableViewCell", for: indexPath) as! ForayNewTableViewCell
cell.setData(name: item.name, desc: type + "ID: " + item.id, img: icon)
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)
}
let detailViewController: ForayDetailViewController = ForayDetailViewController()
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let item = self.sections[indexPath.section].items[indexPath.row]
detailViewController.selectedItem = item
self.navigationController?.pushViewController(detailViewController, animated: true)
}
}
|