aboutsummaryrefslogtreecommitdiff
path: root/foray/ForayTableViewController.swift
blob: 35d80522c9c85644c59df22be181940a29d7352e (plain) (blame)
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
173
174
175
176
177
178
179
180
181
//
//  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]
}

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 = [PenguinItem]()
    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)
        
        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)
    }
    
    // Not sure why need @objc. Is it due to class private/public?
    @objc func doRefresh(sender: AnyObject) {
        reloadApiData()
    }
    
    func reloadApiData() {
        loadApiData(onComplete: { (apiItems) in
            self.items = apiItems
            self.items.sort { (lhs, rhs) in lhs.releaseDate < rhs.releaseDate }
            
            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()
            self.refreshControl?.endRefreshing()
        })
    }
    
    func loadApiData(onComplete: @escaping ([PenguinItem]) -> ()) {
//        return [
//            PenguinItem(type: .item, releaseDate: parseDate("2006-05-26"), id: "mh", name: "Miners Helmet"),
//            PenguinItem(type: .item, releaseDate: parseDate("2010-05-01"), id: "it", name: "Inner Tube"),
//            PenguinItem(type: .item, releaseDate: parseDate("2009-04-24"), id: "tbg", name: "Toboggan"),
//            PenguinItem(type: .item, releaseDate: parseDate("2006-03-29"), id: "spy", name: "Spy Phone"),
//            PenguinItem(type: .item, releaseDate: parseDate("2008-11-18"), id: "bnb", name: "Black Ninja Belt"),
//            PenguinItem(type: .quest, releaseDate: parseDate("2006-05-23"), id: "cmp", name: "Case of the Missing Puffles"),
//            PenguinItem(type: .quest, releaseDate: parseDate("2009-11-16"), id: "gsm", name: "G's Secret Mission"),
//            PenguinItem(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")
        request.cachePolicy = .reloadRevalidatingCacheData // Needed otherwise default caching policy seems not to check properly
        
        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 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
    }
}