简体   繁体   中英

How to fix a value return nil in Swift?

I want to create the TableView from the data from JSON Data but when I tried to take the value it is always return nil at the second print. Could you please explain and give me some advise about this problem? I already tried hard before asking this question but i'm really new to this:(

Best Regards,

import UIKit

struct News: Decodable {
    var status:String
    var totalResults:Int
    var articles:[Articles]?
}

struct Articles: Decodable{
    var source:Source?
    var title:String?
    var description:String?
    var urlToImage:String?
    var publishedAt:String?

}

struct Source: Decodable{
    var name:String?

}

class NewsViewController: UIViewController{
      var a = [String]()

    @IBOutlet weak var TableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Hit API
       let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")

        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            if error == nil {

                do {
                    let result = try JSONDecoder().decode(News.self,from: data!)
                  let totalresult = result.articles!.count


                 for result in result.articles!{
                     let titleUnWrapped: String = result.title ?? ""
                     let urlToImageUnWrapped: String = result.urlToImage ?? ""
                    self.a.append(titleUnWrapped)
                   // print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
                    }
                } catch {
                    print("ERROR")
                }

        }

        }.resume()

       print(self.a) //Return nil <<<<<<<<<<<<<<<<<<<<<<<<< Not Work
}
}

I think the problem is just a lack of experience with closures. This part of the line: URLSession.shared.dataTask(with: url!) sends a request to your url (ie some remote server somewhere) but has no real idea exactly how long it will take to get a response. Instead of waiting and blocking the main thread (unresponsive UI and bad user experience) it makes its request outside the main thread and your code continues to execute while it waits. That is why the last print statement fails but it means that the user can still interact with your app by scrolling a table or whatever.

BUT when the response is eventually received from the remote data source it is passed into your closure and the code inside is executed. So at this point you either have success (data is good) or some failure (check response and error). If you have good data then you can pass it on to some external function to continue working with it. eg

class NewsViewController: UIViewController{

   var a = [String]()

    @IBOutlet weak var TableView: UITableView!

    override func viewDidLoad() {
       super.viewDidLoad()

       //Hit API
       let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")
       URLSession.shared.dataTask(with: url!) { (data, response, error) in
          if error == nil {
             do {
                let result = try JSONDecoder().decode(News.self,from: data!)
                let totalresult = result.articles!.count

                for result in result.articles! {
                   let titleUnWrapped: String = result.title ?? ""
                   let urlToImageUnWrapped: String = result.urlToImage ?? ""
                   self.a.append(titleUnWrapped)
                   // print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
                   self.doSomethingWithResult(with: a) //<<<< Pass `a` out of the closure
                }
             } catch {
                print("ERROR")
             }
           } else {
              // Error is not nil so do something...
              print("ERROR: \(error)")
        }.resume()
     }

     // This method is called from inside the closure only when you have good
     // data returned from your api call.
     func doSomethingWithResult(with goodData: [String]) {
        // Use `a`...
     }
}

So you don't have to worry about trying to access the data too early or waiting for it to finish, the closure won't be called until you have a complete response, either a good one or a bad one. Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM