有没有办法让异步任务在Swift3Xcode8先完成?

Is there a way for asynchronous task to be completed first in Swift 3 Xcode 8?

我是 swift 编程的新手,对 Web 服务不是很熟悉。我想为诗歌创建一个 iOS 移动应用程序。

我能够检索 json 编码的数据,但我的问题是我不能 运行 将它转移到我的表格视图中。当我 运行 它时,我认为问题是因为异步任务。由于是异步任务,tableview(numberOfRows)的委托函数在name.count仍然为0时执行,因为委托函数执行后任务只是运行。这就是为什么我在 TableView 中看不到我的数据的原因......我希望有人能在这里帮助我。我用谷歌搜索并尝试了 Completion Handlers(我不知道它的用途),我尝试将其更改为 Synchronous,这导致我出错。非常感谢!!!

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

    } 
}

在完成块中,当您完成构建 namebio 数组时,通过从主队列调用 reloadData 来刷新 table:

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

我还有其他一些建议:

  • 您应该删除那个 NSURLConnection 代码。它正在执行冗余请求,而您没有对响应做任何事情;另外,它已被弃用 API,应该被删除;

  • 你应该使用URL而不是NSURL

  • 你应该使用URLRequest而不是NSMutableURLRequest

  • 你应该使用String而不是NSString;

  • 您应该使用 Swift ArrayDictionary 而不是 NSArrayNSDictionary;

  • didSelectRowAt 的签名不正确。使用 IndexPath,而不是 NSIndexPath.

  • 您正在更新 URLSession 后台队列中的 namebio 数组。那不是线程安全的。您可以通过从主队列更新这些来解决这个问题,以避免需要进行额外的同步。

  • 我什至建议去掉这两个不同的数组,使用一个自定义对象数组,Poem。这使代码简单得多。它也开辟了新的可能性(例如,如果你想对 name 数组进行排序......你将如何相应地更新单独的 bio 数组;使用单个自定义对象,这个问题就会消失) .

因此:

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: [=11=]) }
                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)
    }
}