-快速:异步+ JSON +完成+ DispatchGroup

时间:2019-02-12 14:50:31

标签: json swift asynchronous dispatch-async completion

我的viewcontroller-class中的代码是在之前执行的,即使在func中有一个用于下载JSON和DispatchGroup()的完成处理程序之前,JSON-download-process也已准备就绪。我将JSON数据存储在一个名为“ fetchedModules”的数组中,在这种情况下,它填充了11个项目。为什么会这样?

result in console:
---> in Class PostCell - func numberOfSections: 0
JSON call finished 

// ViewController
override func viewDidLoad() {
    super.viewDidLoad()

    let group = DispatchGroup()

    group.enter()

    self.fetchJSON()
    // here calling downloadJSONasync

    group.leave()

    group.notify(queue: .main)  {
        print("JSON call finished")
    }

    ...

    // networkService with completion
    func downloadJSONasync(searchItem: String, completion: @escaping ([NSDictionary]) -> Void) {

    //request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData

    request.httpMethod = "GET"

    let configuration = URLSessionConfiguration.default
    //let session = URLSession(configuration: configuration, delegate: nil)

    let session = URLSession(configuration: configuration)

    let task = session.dataTask(with: request, completionHandler: {(data, response, error) in

        guard let data = data, error == nil else { return }

        if (error != nil) {
            print("error!")
        }

        else{

            do {

                let fetchedData = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [NSDictionary]

                completion(fetchedData)

            }

            catch {
                print("error")
            }

        }

    })

    task.resume()

}

// call in viewController
override func numberOfSections(in tableView: UITableView) -> Int {

    print("---> in Class PostCell - func numberOfSections: \(String(describing: fetchedModules.count))")
    return fetchedModules.count

// code of fetchJSON
func fetchJSON()
{

    let baseurl = AppConstants.Domains.baseurl // https://m.myapp2go.de

    let compositURL = baseurl + "getmodules_noItems.php?id=\(AppConstants.appString.startString)"

    let encodedUrl : String! = compositURL.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) // remove the spaces in the url string for safty reason

    let JSONurl = URL(string: encodedUrl)! // convert the string into url

    var JSONrequest = URLRequest(url: JSONurl) // make request

    JSONrequest.httpMethod = "GET"

    //JSONrequest.cachePolicy = .reloadIgnoringCacheData

    let networkService = NetworkService(request: JSONrequest)

    networkService.downloadJSONasync(searchItem: AppConstants.appString.startString, completion: { (fetchedData) in

        fetchedModules.removeAll()

        DispatchQueue.main.async {

            for eachFetchedModul in fetchedData {

                let eachModul = eachFetchedModul

                if

                    let custid    = eachModul["custid"]    as? String,
                    let modulcat  = eachModul["modulcat"]  as? String,
                    let modulname = eachModul["modulname"] as? String,
                    let comment   = eachModul["comment"]   as? String

                {

                    fetchedModules.append(CModules(custid: custid, modulcat: modulcat, modulname: modulname, comment: comment))

                    print(custid)
                    print(modulcat)
                    print(modulname)
                    print(comment)

                    print("---------------------")
                }

            }// for end

            // ... somehow set data source array for your table view
            self.tableView.reloadData()
        }// dispatch



    }


)} // func end

2 个答案:

答案 0 :(得分:1)

您的表格视图从一开始就没有任何数据,因为尚未提取任何数据。没关系,表格视图没有单元格。您只需要reloadData表视图,因为现在您已将元素追加到表视图的数据源数组中,现在应该显示此数据。


请不要为此使用DispatchGroup,只需在接收数据后使用方法的completion参数和completion闭包内部,为表视图设置数据源数组然后...重新加载表格视图的数据

downloadJSONasync(searchItem: "someString") { dictionary in
    DispatchQueue.main.async { // don't forget that your code doesn't run on the main thread
        // ... somehow set data source array for your table view
        self.tableView.reloadData()
    }
}

请注意,您应该避免使用NSDictonary,而应该使用Dictionary。同样在Swift 4+中,您可以使用Codable代替JSONSerialization

答案 1 :(得分:1)

因为fetchJSON会在下载JSON之前立即返回。效果是输入DispatchGroup并立即离开,而无需等待JSON:

group.enter()
self.fetchJSON() // returns immediately
group.leave()    // the JSON has yet to be downloaded

要等到JSON到达后,将完成处理程序添加到fetchJSON

override func viewDidLoad() {
    group.enter()
    self.fetchJSON { 
        group.notify(queue: .main)  {
            print("JSON call finished")
        }
        group.leave()
    }
}

// Change the data type of the completion handler accordingly
func fetchJSON(completionHandler: @escaping (Data?) -> Void) {
    // ...
    networkService.downloadJSONasync(searchItem: AppConstants.appString.startString) { fetchedData in
        defer { completionHandler(fetchedData) }
        // ...
    }
)

使用defer确保无论外部闭包如何返回,都将始终调用完成处理程序。我不清楚您为什么在这里使用DispatchGroup,因为没有等待,但我一直将其保留在可以回答您问题的位置。