Class not being called?

Hello,

I was expecting the code below to print the test message "line 25" because the class "API" is being called on line 57. But "line 25" is not being displayed in the debug window, please could you tell me why?

This is the debugging window:

line 93
0
line 93
0
line 93
0
import UIKit
// not sure these 2 below are needed
import SwiftUI
import Combine
struct NewsFeed: Codable {
var id: String
var name: String
var country: String
var type: String
var situation: String
var timestamp: String
}
let urlString = "https://www.notafunnyname.com/jsonmockup.php"
let url = URL(string: urlString)
let session = URLSession.shared
class API: ObservableObject {
let dataTask = session.dataTask(with: url!) { (data, response, error) in
print("line 25")
var dataString = String(data: data!, encoding: String.Encoding.utf8)
if error == nil && data != nil {
// Parse JSON
let decoder = JSONDecoder()
do {
var newsFeed = try decoder.decode([NewsFeed].self, from: data!)
print("line 38")
// print(newsFeed)
// print("line 125")
// print(newsFeed.count)
print(error)
}
catch{
print("Line 46, Error in JSON parsing")
print(error)
}
}
}.resume
// Make the API Call - not sure why but error clears if moved to line above
// dataTask.resume()
}
let myAPIarray = API()
class QuoteTableViewController: UITableViewController {
var newsFeed: [[String: String]] = []
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// let selectedQuote = quotes[indexPath.row]
// performSegue(withIdentifier: "moveToQuoteDetail", sender: selectedQuote)
}
override func viewDidLoad() {
super.viewDidLoad()
// tableView.dataSource = self
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// (viewDidLoad loads after tableView)
// #warning Incomplete implementation, return the number of rows
print("line 93")
print(newsFeed.count)
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let cell = UITableViewCell ()
cell.textLabel?.text = "test"
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
// getPrice()
print("test_segue")
if let quoteViewController = segue.destination as? QuoteDetailViewController{
if let selectedQuote = sender as? String {
quoteViewController.title = selectedQuote
}
}
}
}
Answered by endecotp in 831916022

There are a couple of issues here:

Firstly, when do you think that line 57 will run?

It seems to be declaring a global, which will run when the app starts. This is not something that is often done in an app. It might be safe, or it might not. Really, you want to run this when your table view controller is constructed.

Secondly, you don’t call classes. You call functions and methods. Classes are instantiated to create objects. If you feel you don’t fully understand that, you need to take a step back and learn it properly now. It will make things much clearer going forward.

So in your class API declaration, you declare a member called dataTask and assign URLSession.dataTask.resume to it. Note that you have not written resume(), so the resume function is not being called - all that it is doing is assigning the function itself - not the result of calling it - to the dataTask member.

Since you’re never resuming (i.e. starting) the data task, its completion handler, and hence line 25, will never run.

As for how to fix this - start with the first issue. Get rid of the globals on lines 17-19 and 57. Start your download from some method In the table view controller - probably viewDidLoad. (Honestly, it’s a while since I’ve done anything quite like this so I’m not totally sure of the best place.) You quite probably don’t need an additional class for this; put it all in your view controller.

Accepted Answer

There are a couple of issues here:

Firstly, when do you think that line 57 will run?

It seems to be declaring a global, which will run when the app starts. This is not something that is often done in an app. It might be safe, or it might not. Really, you want to run this when your table view controller is constructed.

Secondly, you don’t call classes. You call functions and methods. Classes are instantiated to create objects. If you feel you don’t fully understand that, you need to take a step back and learn it properly now. It will make things much clearer going forward.

So in your class API declaration, you declare a member called dataTask and assign URLSession.dataTask.resume to it. Note that you have not written resume(), so the resume function is not being called - all that it is doing is assigning the function itself - not the result of calling it - to the dataTask member.

Since you’re never resuming (i.e. starting) the data task, its completion handler, and hence line 25, will never run.

As for how to fix this - start with the first issue. Get rid of the globals on lines 17-19 and 57. Start your download from some method In the table view controller - probably viewDidLoad. (Honestly, it’s a while since I’ve done anything quite like this so I’m not totally sure of the best place.) You quite probably don’t need an additional class for this; put it all in your view controller.

please could you explain why does creating the instance of API not work as a global

Maybe it does work. You don’t see your “line 25” because you didn’t call resume() and the download never happened. Maybe the object was constructed, we don’t know.

The point is that it shouldn‘t be a global because that’s a bad design, not because it won’t work.

Written by endecotp in 831916022
It seems to be declaring a global, which will run when the app starts.

I agree with the overall thrust of your response but I wanna comment on the above. In Swift, globals have a well-defined lifecycle [1], namely that they’re lazily initialised. So, if you have code like this:

let myGlobal: Int = {
print("Hello Cruel World!")
return 42
}()

you won’t see that output until someone references myGlobal.

This lazy initialisation is done under a lock, so the above code will only ever run once, even if multiple threads reference myGlobal concurrently.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Except for global in main.swift, where things get weird.

Class not being called?
 
 
Q