2017-08-26 5 views
0

Je suis très nouveau dans la programmation rapide et je ne suis pas très familier avec les services Web. Je veux créer une application mobile iOS pour les poèmes.Existe-t-il un moyen d'effectuer une tâche asynchrone en premier dans Swift 3 Xcode 8?

J'ai été en mesure de récupérer les données codées json, mais mon problème est que je ne peux pas le transférer dans ma vue de table ici. Quand je l'ai couru, je pense que le problème était dû à la tâche asynchrone. Comme il s'agit d'une tâche asynchrone, les fonctions déléguées de la tableview (numberOfRows) sont exécutées lorsque name.count est toujours 0, car la tâche ne serait exécutée qu'après l'exécution de la fonction déléguée. C'est pourquoi je ne peux pas voir mes données dans mon TableView ... J'espère que quelqu'un peut m'aider ici. J'ai googlé et essayé les gestionnaires d'achèvement (dont je n'ai aucune idée de quoi il est utilisé) et j'ai essayé de le changer en synchrone ce qui m'a conduit à des erreurs .. Merci beaucoup !!!

import UIKit 
import Foundation 

class TimelineViewController: UIViewController { 
    @IBOutlet weak var contentTableView: UITableView! 
    var name = [String]() 
    var bio = [String]() 

    @IBOutlet weak var oPostBtn: UIButton! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     getPoems() 
    } 

    func getPoems() 
    { 
     let url:NSURL = NSURL(string: "http://192.168.1.6/Test/feed1.php")! 
     let request:NSMutableURLRequest = NSMutableURLRequest(url:url as URL) 

     request.httpMethod = "GET" 

     NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main){(response, data, error) in } 

     let task = URLSession.shared.dataTask(with: request as URLRequest) { 
       data, response, error in 

       if error != nil { 
        print("Error = \(error)") 
        return 
       } 
       do { 
        print("Response=\(response)") 

        let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) 
        print("Response data = \(responseString)") 

        //Converting response to NSDictionary 
        var json: NSDictionary! 
        json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary 

        //getting the JSONArray poems from the response 
        let returnPoems: NSArray = json["returnPoems"] as! NSArray 

        print(returnPoems); 


        //looping through all the json objects in the array teams 
        for i in 0 ..< returnPoems.count{ 


         let aObject = returnPoems[i] as! [String : AnyObject] 


         self.name.append(aObject["fullName"] as! String) 
         self.bio.append(aObject["Bio"] as! String) 
         //displaying the data 

         print("Fullname -> ", self.name) 
         print("Bio ->", self.bio) 

         print("===================") 
         print("") 

        } 


       } 
       catch { 
        print("ERROR: \(error)") 
       } 

     } 
     task.resume() 

    } 

} 

extension TimelineViewController: UITableViewDelegate, UITableViewDataSource { 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 


     let cell = contentTableView.dequeueReusableCell(withIdentifier: "PoemCell") 


     cell?.textLabel?.text = name[indexPath.row] 
     cell?.detailTextLabel?.text = bio[indexPath.row] 

     return cell! 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 

     return name.count 
    } 
    func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
     print("You selected cell #\(indexPath.row)!") 

     let indexPath = tableView.indexPathForSelectedRow! 
     let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell 

    } 
} 
+0

Dans votre bloc d'achèvement, lorsque vous avez terminé la construction de la '' name' et tableaux de bio', appelez 'DispatchQueue.main.async {self.tableView.reloadData ()} 'pour avoir une table rafraîchie. – Rob

+0

@Rob Merci beaucoup Rob !! Je n'arrive pas à croire que j'ai passé des heures là-dessus .. Aussi, j'ai fait ce que vous avez dit et vous avez raison! Fonctionne bien. Merci encore! – Nadz

Répondre

1

Dans votre bloc d'achèvement, lorsque vous avez terminé la construction des name et bio tableaux, actualisez la table en appelant reloadData de la file d'attente principale:

DispatchQueue.main.async { 
    self.tableView.reloadData() 
} 

Il y avait quelques autres choses que je suggérerais:

  • Vous devriez enlever cela NSURLConnection code. Il effectue une requête redondante et vous ne faites rien avec la réponse; plus c'est une API obsolète de toute façon et devrait être supprimée; Vous devez utiliser URL au lieu de NSURL; Vous devez utiliser URLRequest au lieu de NSMutableURLRequest; Vous devez utiliser String plutôt que NSString;

  • Vous devez utiliser Swift Array et Dictionary plutôt que NSArray et NSDictionary;

  • Votre signature pour didSelectRowAt est incorrecte. Utilisez IndexPath, et non NSIndexPath. Vous mettez à jour les tableaux et bio de la file d'attente d'arrière-plan URLSession. Ce n'est pas thread-safe. Vous pouvez résoudre ce problème en les mettant à jour dans la file d'attente principale pour éviter d'avoir à effectuer une synchronisation supplémentaire.

  • Je suggère même de se débarrasser de ces deux tableaux différents et avoir un seul tableau d'un objet personnalisé, Poem. Cela rend le code beaucoup plus simple. Cela ouvre aussi de nouvelles possibilités (par exemple, si vous voulez trier le tableau name ... comment mettriez-vous à jour le tableau séparé bio en conséquence, utilisez un seul objet personnalisé et ce problème disparaîtra).

Ainsi:

struct Poem { 
    let name: String 
    let bio: String 

    init?(dictionary: [String: Any]) { 
     guard let name = dictionary["fullName"] as? String, 
      let bio = dictionary["Bio"] as? String else { 
       print("Did not find fullName/Bio") 
       return nil 
     } 

     self.name = name 
     self.bio = bio 
    } 
} 

class TimelineViewController: UIViewController { 

    @IBOutlet weak var contentTableView: UITableView! 

    var poems = [Poem]() 

    @IBOutlet weak var oPostBtn: UIButton! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     getPoems() 
    } 

    func getPoems() { 
     let url = URL(string: "http://192.168.1.6/Test/feed1.php")! 
     var request = URLRequest(url: url) 

     request.httpMethod = "GET" 

     let task = URLSession.shared.dataTask(with: request) { data, response, error in 
      guard let data = data, error == nil else { 
       print("Error = \(error?.localizedDescription ?? "Unknown error")") 
       return 
      } 

      if let response = response { 
       print("Response=\(response)") 
      } 

      if let responseString = String(data: data, encoding: .utf8) { 
       print("Response data = \(responseString)") 
      } 

      // Converting response to Dictionary 

      guard let json = try? JSONSerialization.jsonObject(with: data), 
       let responseObject = json as? [String: Any], 
       let returnPoems = responseObject["returnPoems"] as? [[String: Any]] else { 
        print("did not find returnPoems") 
        return 
      } 

      print(returnPoems) 

      // dispatch the update of model and UI to the main queue 

      DispatchQueue.main.async { 
       self.poems = returnPoems.flatMap { Poem(dictionary: $0) } 
       self.contentTableView.reloadData() 
      } 
     } 

     task.resume() 
    } 

} 

extension TimelineViewController: UITableViewDelegate, UITableViewDataSource { 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = contentTableView.dequeueReusableCell(withIdentifier: "PoemCell", for: indexPath) 

     let poem = poems[indexPath.row] 

     cell.textLabel?.text = poem.name 
     cell.detailTextLabel?.text = poem.bio 

     return cell 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return poems.count 
    } 

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     print("You selected cell #\(indexPath.row)!") 

     // let indexPath = tableView.indexPathForSelectedRow!       // why do this? ... the indexPath was passed to this function 
     // let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell // why do this? ... you don't care about the cell ... go back to you model 

     let poem = poems[indexPath.row] 

     // do something with poem, e.g. 

     print(poem) 
    } 
}