有没有办法让异步任务在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
}
}
在完成块中,当您完成构建 name
和 bio
数组时,通过从主队列调用 reloadData
来刷新 table:
DispatchQueue.main.async {
self.tableView.reloadData()
}
我还有其他一些建议:
您应该删除那个 NSURLConnection
代码。它正在执行冗余请求,而您没有对响应做任何事情;另外,它已被弃用 API,应该被删除;
你应该使用URL
而不是NSURL
;
你应该使用URLRequest
而不是NSMutableURLRequest
;
你应该使用String
而不是NSString
;
您应该使用 Swift Array
和 Dictionary
而不是 NSArray
和 NSDictionary
;
您 didSelectRowAt
的签名不正确。使用 IndexPath
,而不是 NSIndexPath
.
您正在更新 URLSession
后台队列中的 name
和 bio
数组。那不是线程安全的。您可以通过从主队列更新这些来解决这个问题,以避免需要进行额外的同步。
我什至建议去掉这两个不同的数组,使用一个自定义对象数组,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)
}
}
我是 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
}
}
在完成块中,当您完成构建 name
和 bio
数组时,通过从主队列调用 reloadData
来刷新 table:
DispatchQueue.main.async {
self.tableView.reloadData()
}
我还有其他一些建议:
您应该删除那个
NSURLConnection
代码。它正在执行冗余请求,而您没有对响应做任何事情;另外,它已被弃用 API,应该被删除;你应该使用
URL
而不是NSURL
;你应该使用
URLRequest
而不是NSMutableURLRequest
;你应该使用
String
而不是NSString
;您应该使用 Swift
Array
和Dictionary
而不是NSArray
和NSDictionary
;您
didSelectRowAt
的签名不正确。使用IndexPath
,而不是NSIndexPath
.您正在更新
URLSession
后台队列中的name
和bio
数组。那不是线程安全的。您可以通过从主队列更新这些来解决这个问题,以避免需要进行额外的同步。我什至建议去掉这两个不同的数组,使用一个自定义对象数组,
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)
}
}