-- swift: 异步 + JSON + 完成 + DispatchGroup
-- swift: async + JSON + completion + DispatchGroup
我的 viewcontroller-class 中的代码在 在 JSON-下载进程准备就绪之前执行,即使有函数中的完成处理程序用于下载 JSON 一个 DispatchGroup()。我将 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
您的 table 视图从一开始就没有任何数据,因为尚未获取任何数据。所以没关系,table 视图没有单元格。您只需要 table 视图的 reloadData
,因为现在您将元素附加到 table 视图的数据源数组,现在您应该显示此数据。
请不要为此使用 DispatchGroup
,只需使用方法的 completion
参数,并在收到数据后在 completion
闭包内部设置数据源数组table 查看然后...重新加载 table 视图的数据
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
.
因为 fetchJSON
returns 立即,在 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
确保完成处理程序将始终被调用,无论外部闭包如何 returns。我不清楚为什么你在这里使用 DispatchGroup
因为没有等待,但我把它留在原地回答你的问题。
我的 viewcontroller-class 中的代码在 在 JSON-下载进程准备就绪之前执行,即使有函数中的完成处理程序用于下载 JSON 一个 DispatchGroup()。我将 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
您的 table 视图从一开始就没有任何数据,因为尚未获取任何数据。所以没关系,table 视图没有单元格。您只需要 table 视图的 reloadData
,因为现在您将元素附加到 table 视图的数据源数组,现在您应该显示此数据。
请不要为此使用 DispatchGroup
,只需使用方法的 completion
参数,并在收到数据后在 completion
闭包内部设置数据源数组table 查看然后...重新加载 table 视图的数据
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
.
因为 fetchJSON
returns 立即,在 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
确保完成处理程序将始终被调用,无论外部闭包如何 returns。我不清楚为什么你在这里使用 DispatchGroup
因为没有等待,但我把它留在原地回答你的问题。