The main thing I wonder about, is, that I'm able to log self.schedule (.monday)
after the download JSON function successfully, but instantly gets nil
when the tables should be refreshed.
There are currently two separate UITableViews in my ScheduleViewController
, where the tables should be filled differently with the downloaded data by the view controller it's downloadSchedule()
function.
After a successful URLSession
task, the received data will be stored in new variable called json
and is printed in the console afterwards. Everything works well.
Now, when the downloadSchedule()
function finished it's request and the next function to reload the tables is called, I want to count the the data for numberOfRowsInSection
, but I'm getting a Thread 1: Fatal error: Unexpectedly found nil while unwrapping an optional value
.
Although the variable was previously filled with the downloaded data, it is now suddenly empty when I want to update the tables. How can this happen?
I declare my variable in my ScheduleViewController
like so:
var schedule: Schedule?
The downloadSchedule()
function gets executed after viewDidLoad()
:
func downloadSchedule(completed: @escaping () -> Void) {
let url = URL(string: "example.com/meta/schedule")
URLSession.shared.dataTask(with: url!) { data, _, error in
if error == nil {
do {
self.schedule = try JSONDecoder().decode(Schedule.self, from: data!)
DispatchQueue.main.async {
print("executed")
completed()
}
} catch {
print("JSON Error")
}
}
}.resume()
}
downloadSchedule {
self.mondayTableView.reloadData()
self.tuesdayTableView.reloadData()
}
An example what happens first to update the tables:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of items in the sample data structure.
var count:Int?
if tableView == self.mondayTableView {
count = schedule?.monday.count
}
if tableView == self.tuesdayTableView {
count = schedule?.tuesday.count
}
return count!
}
These are my structs for Schedule and Day:
// MARK: - Schedule
struct Schedule: Codable {
let monday: [Day]
let tuesday: [Day]
let wednesday: [Day]
let thursday: [Day]
let friday: [Day]
let saturday: [Day]
let sunday: [Day]
let nextmonday: [Day]
let nexttuesday: [Day]
let nextwednesday: [Day]
let nextthursday: [Day]
let nextfriday: [Day]
let nextsaturday: [Day]
let nextsunday: [Day]
let airtimeAPIVersion: String
enum CodingKeys: String, CodingKey {
case monday, tuesday, wednesday, thursday, friday, saturday, sunday, nextmonday, nexttuesday, nextwednesday, nextthursday, nextfriday, nextsaturday, nextsunday
case airtimeAPIVersion = "AIRTIME_API_VERSION"
}
}
// MARK: - Day
struct Day: Codable {
let startTimestamp: String?
let endTimestamp: String?
let name: String?
let dayDescription: String?
let id: Int?
let instanceID: Int?
let instanceDescription: String?
let record: Int?
let url: String?
let imagePath: String?
let starts: String?
let ends: String?
enum CodingKeys: String, CodingKey {
case startTimestamp = "start_timestamp"
case endTimestamp = "end_timestamp"
case name = "name"
case dayDescription = "description"
case id = "id"
case instanceID = "instance_id"
case instanceDescription = "instance_description"
case record = "record"
case url = "url"
case imagePath = "image_path"
case starts = "starts"
case ends = "ends"
}
}
{
"monday": [
{
"start_timestamp": "2020-06-01 01:35:00",
"end_timestamp": "2020-06-01 01:37:37",
"name": "TEST",
"description": "",
"id": 174,
"instance_id": 950,
"instance_description": "",
"record": 0,
"url": "",
"image_path": "example.com/api/show-logo?id=174",
"starts": "2020-06-01 01:35:00",
"ends": "2020-06-01 01:37:37"
}
],
"tuesday": [
...
]
}
You shouldn't to return potential nil in the tableView:numberOfRowsInSection
method. Because it should be called at initial tableview load data and 100% you will get crash. You may return 0
.
So:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of items in the sample data structure.
guard let schedule = schedule else { return 0 }
var count:Int = 0
if tableView == self.mondayTableView {
count = schedule.monday.count
}
if tableView == self.tuesdayTableView {
count = schedule.tuesday.count
}
return count
}
Also, are you sure that
schedule.monday
or . tuesday
is not nil after loadData? self.schedule
is nil in the tableView:numberOfRowsInSection
? UITableViewDelegate
(in this
viewcontroller)?Also completion handler in your loadData
will be newer called if you get an error of url request or parsing json. Add completed()
to this places or move your one to the bottom:
func downloadSchedule(completed: @escaping () -> Void) {
let url = URL(string: "https://api/....")
URLSession.shared.dataTask(with: url!) { data, _, error in
if error == nil,
let data = data {
do {
self.schedule = try JSONDecoder().decode(Schedule.self, from: data)
print("JSON \(self.schedule?.monday)")
} catch {
print("JSON Error")
}
}
DispatchQueue.main.async {
completed()
}
}.resume()
}