skip to Main Content

I have the following problem: I want to make an app which displays information about the sun. I have managed to print the Sunrise in the console, however that’s not the final goal. I want to display it in an NSTextField. This is where it starts to get problematic.

I have tried to add self.sunrise1.stringValue = sunrise in the private func getData(). Apparently this doesn’t work and Xcode tells me,

NSControl.stringValue must be used in main thread only

So now I thought I have to return the sunrise variable. But when I add return(sunrise) Xcode shows an error:

unexpected non-void return error

So my question is, how do I display the stringValue of sunrise in my label sunrise1?

class ViewController: NSViewController {
    
    @IBOutlet weak var sunrise1: NSTextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = "https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400"
        getData(from: url)
        

        // Do any additional setup after loading the view.
    }
    
    private func getData(from url: String) {
        
        let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: {data, response, error in
            
            guard let data = data, error == nil else {
                print("something went wrong")
                return
            }
            
            var result: Response?
            do {
                result = try JSONDecoder().decode(Response.self, from: data)
            }
            catch {
                print("failed to convert (error.localizedDescription)")
            }
            
            guard let json = result else {
                return
            }
            
            let sunrise = json.results.sunrise
        
            print(sunrise)
            
        })task.resume()
    }

}

struct Response: Codable {
    let results: MyResult
    let status: String
}

struct MyResult: Codable {
    let sunrise: String
    let sunset: String
    let solar_noon: String
    let day_length: String
    let civil_twilight_begin: String
    let civil_twilight_end: String
    let nautical_twilight_begin: String
    let nautical_twilight_end: String
    let astronomical_twilight_begin: String
    let astronomical_twilight_end: String
    
}

2

Answers


  1. Make assignment on main queue, like

    DispatchQueue.main.async { [weak self] in
       self?.sunrise1.stringValue = sunrise
    }
    
    Login or Signup to reply.
  2. Actually, is better to have completion in your func getData, so when it finished working you will update your label in completion.
    As reference I may propose something like:

      func getData(fromUrl url: String, completion: @escaping (String) -> ()) {
        let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
          guard let data = data, error == nil else {
            print("something went wrong")
            return
          }
          var result: Response?
          do {
            result = try JSONDecoder().decode(Response.self, from: data)
          }
          catch {
            print("failed to convert (error.localizedDescription)")
          }
          guard let json = result else {
            return
          }
          let sunrise = json.results.sunrise
          completion(sunrise)
        })
        task.resume()
      }
    

    And now you can call you method in, for example, viewDidLoad() and update your label like that:

    self.getData(fromUrl: "") { sunrise in
      DispatchQueue.main.async { [weak self] in
         self?.sunrise1.stringValue = sunrise
      }
    }
    

    To get more information you may learn about completion block/handlers, functions with completions and why we must update UI in main thread.

    But when I add return(sunrise) Xcode shows an error : "unexpected
    non-void return error".

    If you want your func to return something, you have to declare about that in method signature, for example:

    func itReturnInt() -> Int {
      return 1
    }
    

    will return 1 which is Int, but if you try to return other type, you will get compilation error.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search