【发布时间】:2020-12-18 16:18:39
【问题描述】:
我是 Swift 新手,通常缺乏编程经验。目前我正在开发一个项目,试图在视图控制器上显示星球大战角色列表,但我在通过网络管理器传递数据时遇到了一些问题。当我运行程序时,我无法在屏幕上显示名称标签。
我检查了 tableView 单元格,标签与viewController 连接。我觉得这个问题与网络管理员有关,但我自己无法弄清楚。
var charactersArray: [Characters] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getCharacters(){
DispatchQueue.main.async {
self.starWarTableViewController.reloadData()
}
}
self.title = "Star War Characters"
// print(charactersArray), returns an empty array
}
private func getURL() -> String {
return "https://swapi.dev/api/people/"
}
func getCharacters(completion: @escaping () -> Void) {
self.starWarTableViewController.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
self.starWarTableViewController.dataSource = self
self.starWarTableViewController.delegate = self
DispatchQueue.global(qos: .background).async {
for i in 1...20 {
NetworkingManager.shared.getDecodedObject(from: self.getURL() + "\(i)"){
(characters: Characters?, error) in
guard let characters = characters else{ return }
self.charactersArray.append(characters)
print(self.charactersArray) //this will return an array list of characters names
}
}
print(self.charactersArray) // here the characterArray is empty
}
completion()
}
根据我的发现,问题出现在我的网络管理器或表格视图单元格中
enum NetworkError: Error {
case invalidURLString
}
final class NetworkingManager{
static let shared = NetworkingManager()
private init(){
}
func getDecodedObject <T: Decodable> (from urlString: String, completion: @escaping (T?, Error?) -> Void){
guard let url = URL(string: urlString) else {
completion(nil, NetworkError.invalidURLString)
return
}
URLSession.shared.dataTask(with: url){
(data, response, error) in
guard let data = data else{
completion(nil, error)
return }
guard let characters = try? JSONDecoder().decode(T.self, from: data) else{ return }
completion(characters, nil)
}.resume()
}
}
class TableViewCell: UITableViewCell {
@IBOutlet weak var nameLabel: UILabel!
func configure (with characters: Characters) {
self.nameLabel.text = characters.name
}
}
这里是字符
struct Characters: Decodable {
let name: String
enum CodingKeys: String, CodingKey {
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
// print(name), this will return a list of character names
}
}
这是表格视图扩展
extension ViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.charactersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.configure(with: self.charactersArray[indexPath.row])
// cell.nameLabel.text = "Hi"
return cell
}
}
【问题讨论】:
-
你必须阅读异步编程。简而言之,您现在调用
completion()时,尚未检索到数据(只会在稍后的某个时间点检索到 - 即异步)。此外,因为您要从多个 URL 获取数据,所以您需要“等待”直到它们都被检索到(阅读 DispatchGroup.notify),然后才调用completion() -
与手头的问题无关,我会将
Characters重命名为单数(即没有尾随s),因为它显然代表单个命名字符,而不是它们的集合。我会避免使用Character,因为它与同名的标准类型冲突。我不知道这些名字代表了什么样的“字符”,所以我无法给出具体的建议,只是对模型类型命名的一些想法...... -
下面我已经回答了有关如何执行 20 次查询、按排序顺序收集结果以及在完成后更新模型和 UI 的问题。话虽如此,正确的解决方案通常是在您的服务器中写入和端点以在单个响应中返回 20 个值。我们在这里做的事情效率很低。
标签: ios swift networking decode