Ios Swift 5:显示在表视图关闭中检索到的数据
我已经在函数中的闭包中成功地从API获得了一些数据。我需要在我的故事板上的表格中显示数据 我知道它不显示的原因是因为数据保存在闭包中,除非我有一个完成处理程序,否则我无法从外部访问它。我在这里读了很多关于堆栈溢出的其他问题,但我不能真正理解它。我也尝试过重新加载该表,但它只返回一个错误“意外发现为零” 索赔财产Ios Swift 5:显示在表视图关闭中检索到的数据,ios,swift,api,networking,Ios,Swift,Api,Networking,我已经在函数中的闭包中成功地从API获得了一些数据。我需要在我的故事板上的表格中显示数据 我知道它不显示的原因是因为数据保存在闭包中,除非我有一个完成处理程序,否则我无法从外部访问它。我在这里读了很多关于堆栈溢出的其他问题,但我不能真正理解它。我也尝试过重新加载该表,但它只返回一个错误“意外发现为零” 索赔财产 class ClaimProperties { var id: Int var date: String var status: String init
class ClaimProperties {
var id: Int
var date: String
var status: String
init(id: Int, date: String, status: String) {
self.id = id
self.date = date
self.status = status
}
}
仪表板控制器
struct Claims: Decodable {
let id: Int
let submission_date: String
let status: String
init(json: [String:Any]) {
id = json["id"] as? Int ?? -1
submission_date = json["submission_date"] as? String ?? ""
status = json["status"] as? String ?? ""
}
}
class DashboardController: UIViewController, GIDSignInUIDelegate {
@IBOutlet weak var tableView: UITableView!
var tempArray: [ClaimProperties] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
// getTokenFromAPI() gets called form AppDelegate.swift once the user logs in via Google API
func getTokenFromAPI(usersAppToken:String) {
guard let urlString = URL(string: "https://claim.ademo.work/claims/") else { return }
var requestAPI = URLRequest(url: urlString)
requestAPI.httpMethod = "GET"
requestAPI.addValue("application/json", forHTTPHeaderField: "Content-Type")
requestAPI.addValue("application/json", forHTTPHeaderField: "Accept")
requestAPI.setValue("Bearer \(usersAppToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: requestAPI) {
(data, response, error) in
if let data = data {
do {
let json = try JSONDecoder().decode([Claims].self, from: data)
for n in 0..<json.count {
self.tempArray.append(ClaimProperties(id: json[n].id, date: json[n].submission_date, status: json[n].status))
}
// This is the data I'm trying to display -> tempArray
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
}
}
extension DashboardController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tempArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ClaimCell", for: indexPath) as! AllMyClaimsTable
cell.idField.text = String(tempArray[indexPath.section].id)
cell.dateField.text = tempArray[indexPath.section].date
cell.statusField.text = tempArray[indexPath.section].status
return cell
}
}
import UIKit
struct Claims: Decodable {
let id: Int
let submission_date: String
let status: String
init(json: [String:Any]) {
id = json["id"] as? Int ?? -1
submission_date = json["submission_date"] as? String ?? ""
status = json["status"] as? String ?? ""
}
}
class DashboardController: UIViewController {
@IBOutlet weak var dashboardMyClaim: UITableView!
var myClaimArray: [PropertyExistingClaim] = []
var idToken = ""
var email = ""
var appToken = ""
override func viewDidLoad() {
super.viewDidLoad()
idToken = AppDelegate.originalAppDelegate.userIdToken
email = AppDelegate.originalAppDelegate.userEmail
tableSettings()
getAppToken()
}
func tableSettings() {
dashboardMyClaim.delegate = self
dashboardMyClaim.dataSource = self
}
// Get user's App Token from Google API
func getAppToken() {
guard let urlString = URL(string: "https://claim.ademo.work/sessions/") else { return }
var requestAPI = URLRequest(url: urlString)
let parameters: [String: Any] = ["email":email, "platform":"web", "id_token":idToken]
requestAPI.httpMethod = "POST"
requestAPI.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {return}
requestAPI.httpBody = httpBody
requestAPI.timeoutInterval = 20
URLSession.shared.dataTask(with: requestAPI) { (data, response, error) in
if let data = data {
do {
let jsonResult = try JSONDecoder().decode(SignIn.self, from: data)
self.getClaimFromAPI(usersAppToken: jsonResult.app_token)
} catch {
print(error)
}
}
}.resume()
}
// Use the App Token to GET claims from the database / API
func getClaimFromAPI(usersAppToken:String) {
guard let urlString = URL(string: "https://claim.ademo.work/claims/") else { return }
var requestAPI = URLRequest(url: urlString)
requestAPI.httpMethod = "GET"
requestAPI.addValue("application/json", forHTTPHeaderField: "Content-Type")
requestAPI.addValue("application/json", forHTTPHeaderField: "Accept")
requestAPI.setValue("Bearer \(usersAppToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: requestAPI) { [weak self] (data, response, error) in
if let data = data {
do {
let json = try JSONDecoder().decode([Claims].self, from: data)
for n in 0..<json.count {
self!.myClaimArray.append(PropertyExistingClaim(id: json[n].id, date: json[n].submission_date, desc: "-", amount: "-", amountMyr: "-", currency: "-", status: json[n].status))
}
DispatchQueue.main.async {
self!.dashboardMyClaim.reloadData()
}
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToNewClaimDashboard" {
let destinationVC = segue.destination as? NewClaimDashboardController
}
}
}
extension DashboardController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
myClaimArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = dashboardMyClaim.dequeueReusableCell(withIdentifier: "DashboardMyClaim", for: indexPath) as! TableDashboardMyClaim
cell.dateField.text = "Date: \(myClaimArray[indexPath.row].date)"
cell.descriptionField.text = "Description: \(myClaimArray[indexPath.row].desc)"
cell.amountMyrField.text = "RM \(myClaimArray[indexPath.row].amountMyr)"
cell.idField.text = "#\(myClaimArray[indexPath.row].id)"
cell.statusField.text = "\(myClaimArray[indexPath.row].status)"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120.0
}
}
struct声明:可解码{
让id:Int
提交日期:String
let状态:字符串
init(json:[字符串:任意]){
id=json[“id”]as?Int???-1
提交日期=json[“提交日期”]作为?字符串??“
status=json[“status”]作为?字符串??“
}
}
类仪表板控制器:UIViewController、GIDSignInUIDelegate{
@IBVAR表格视图:UITableView!
var tempArray:[clairproperties]=[]
重写func viewDidLoad(){
super.viewDidLoad()
tableView.delegate=self
tableView.dataSource=self
}
//一旦用户通过Google API登录,getTokenFromAPI()将以AppDelegate.swift的形式被调用
func getTokenFromAPI(usersaptoken:String){
guard let urlString=URL(字符串:https://claim.ademo.work/claims/)否则{return}
var requestAPI=URLRequest(url:urlString)
requestAPI.httpMethod=“GET”
addValue(“应用程序/json”,forHTTPHeaderField:“内容类型”)
addValue(“application/json”,用于httpheaderfield:“Accept”)
requestAPI.setValue(“承载者\(usersAppToken)”,用于HttpHeaderField:“授权”)
URLSession.shared.dataTask(带:requestAPI){
(数据、响应、错误)
如果let data=data{
做{
让json=try JSONDecoder().decode([Claims].self,from:data)
对于0中的n..将您的重新加载
行移动到for循环下
URLSession.shared.dataTask(with:requestAPI){[weak self](数据、响应、错误)位于
如果let data=data{
做{
让json=try JSONDecoder().decode([Claims].self,from:data)
对于0..中的n,表中的行表示tempArray
中的数据,因此下标应该是indexPath.row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ClaimCell", for: indexPath) as! AllMyClaimsTable
cell.idField.text = String(tempArray[indexPath.row].id)
cell.dateField.text = tempArray[indexPath.row].date
cell.statusField.text = tempArray[indexPath.row].status
return cell
}
编辑:另外,添加一个didSet
属性观察器,该观察器将在数组更改时自动重新加载表视图。虽然这只是一个临时的解决方法,以说明一个要点,并更轻松地调试代码。为每次插入或删除调用tableView.reloadData()
,效率非常低
var tempArray: [ClaimProperties] = [] {
didSet {
print("didSet...")
print(tempArray)
tableView.reloadData()
}
}
如果您现在在cellForRowAt
中添加一个打印日志,它将证明tempArray的值正在按照您的意愿进行设置和保存
print("In cellForRowAt...")
print(tempArray)
说明:
打印的原因(tempArray)
不会在其他任何地方打印更新后的值,因为tableView的数据源方法在tableView首次出现时只被调用一次。它们只有在您专门调用reloadData
方法时才会再次被调用。如果您添加didSet
代码,则每次添加到数组中时都应该重新加载,然后de>cellForRowAt
将在闭包完成执行时再次调用,这将打印更新的(非空)tempArray
如果在上述修复之后该表仍然无法显示,那么我可以自信地说问题出在别处。解决方案:
在搜索并修改我的代码后,以下是可行的解决方案:
仪表板控制器
struct Claims: Decodable {
let id: Int
let submission_date: String
let status: String
init(json: [String:Any]) {
id = json["id"] as? Int ?? -1
submission_date = json["submission_date"] as? String ?? ""
status = json["status"] as? String ?? ""
}
}
class DashboardController: UIViewController, GIDSignInUIDelegate {
@IBOutlet weak var tableView: UITableView!
var tempArray: [ClaimProperties] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
// getTokenFromAPI() gets called form AppDelegate.swift once the user logs in via Google API
func getTokenFromAPI(usersAppToken:String) {
guard let urlString = URL(string: "https://claim.ademo.work/claims/") else { return }
var requestAPI = URLRequest(url: urlString)
requestAPI.httpMethod = "GET"
requestAPI.addValue("application/json", forHTTPHeaderField: "Content-Type")
requestAPI.addValue("application/json", forHTTPHeaderField: "Accept")
requestAPI.setValue("Bearer \(usersAppToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: requestAPI) {
(data, response, error) in
if let data = data {
do {
let json = try JSONDecoder().decode([Claims].self, from: data)
for n in 0..<json.count {
self.tempArray.append(ClaimProperties(id: json[n].id, date: json[n].submission_date, status: json[n].status))
}
// This is the data I'm trying to display -> tempArray
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
}
}
extension DashboardController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tempArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ClaimCell", for: indexPath) as! AllMyClaimsTable
cell.idField.text = String(tempArray[indexPath.section].id)
cell.dateField.text = tempArray[indexPath.section].date
cell.statusField.text = tempArray[indexPath.section].status
return cell
}
}
import UIKit
struct Claims: Decodable {
let id: Int
let submission_date: String
let status: String
init(json: [String:Any]) {
id = json["id"] as? Int ?? -1
submission_date = json["submission_date"] as? String ?? ""
status = json["status"] as? String ?? ""
}
}
class DashboardController: UIViewController {
@IBOutlet weak var dashboardMyClaim: UITableView!
var myClaimArray: [PropertyExistingClaim] = []
var idToken = ""
var email = ""
var appToken = ""
override func viewDidLoad() {
super.viewDidLoad()
idToken = AppDelegate.originalAppDelegate.userIdToken
email = AppDelegate.originalAppDelegate.userEmail
tableSettings()
getAppToken()
}
func tableSettings() {
dashboardMyClaim.delegate = self
dashboardMyClaim.dataSource = self
}
// Get user's App Token from Google API
func getAppToken() {
guard let urlString = URL(string: "https://claim.ademo.work/sessions/") else { return }
var requestAPI = URLRequest(url: urlString)
let parameters: [String: Any] = ["email":email, "platform":"web", "id_token":idToken]
requestAPI.httpMethod = "POST"
requestAPI.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {return}
requestAPI.httpBody = httpBody
requestAPI.timeoutInterval = 20
URLSession.shared.dataTask(with: requestAPI) { (data, response, error) in
if let data = data {
do {
let jsonResult = try JSONDecoder().decode(SignIn.self, from: data)
self.getClaimFromAPI(usersAppToken: jsonResult.app_token)
} catch {
print(error)
}
}
}.resume()
}
// Use the App Token to GET claims from the database / API
func getClaimFromAPI(usersAppToken:String) {
guard let urlString = URL(string: "https://claim.ademo.work/claims/") else { return }
var requestAPI = URLRequest(url: urlString)
requestAPI.httpMethod = "GET"
requestAPI.addValue("application/json", forHTTPHeaderField: "Content-Type")
requestAPI.addValue("application/json", forHTTPHeaderField: "Accept")
requestAPI.setValue("Bearer \(usersAppToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: requestAPI) { [weak self] (data, response, error) in
if let data = data {
do {
let json = try JSONDecoder().decode([Claims].self, from: data)
for n in 0..<json.count {
self!.myClaimArray.append(PropertyExistingClaim(id: json[n].id, date: json[n].submission_date, desc: "-", amount: "-", amountMyr: "-", currency: "-", status: json[n].status))
}
DispatchQueue.main.async {
self!.dashboardMyClaim.reloadData()
}
} catch let error {
print("Localized Error: \(error.localizedDescription)")
print("Error: \(error)")
}
}
}.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToNewClaimDashboard" {
let destinationVC = segue.destination as? NewClaimDashboardController
}
}
}
extension DashboardController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
myClaimArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = dashboardMyClaim.dequeueReusableCell(withIdentifier: "DashboardMyClaim", for: indexPath) as! TableDashboardMyClaim
cell.dateField.text = "Date: \(myClaimArray[indexPath.row].date)"
cell.descriptionField.text = "Description: \(myClaimArray[indexPath.row].desc)"
cell.amountMyrField.text = "RM \(myClaimArray[indexPath.row].amountMyr)"
cell.idField.text = "#\(myClaimArray[indexPath.row].id)"
cell.statusField.text = "\(myClaimArray[indexPath.row].status)"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120.0
}
}
特别感谢所有在这个问题上帮助过我的人。我真的很感激!:)粗略检查(在cellForRow实现中),也许应该是indexPath.row而不是indexPath.section。现在section
为0,所以如果tempArray
有一些值,则实现类似tempArray[indexPath.section]
必须在第一(0)个索引中返回值。但是OP说,意外地发现出现了nil
错误。我同意您@emrcftci的观点,但为了正确显示数据,cellForRow
中的实现也应该被更正。@Wattholm是的,您是对的,应该使用tempArray[indexPath.row]完成
而不是tempArray[indexPath.section]
self?.tableView.reloadData()给出了一个错误:“无法对'DashboardController'类型的非可选值使用可选链接”,因此我删除了问号。之后,我得到了一个错误“隐式展开可选值时意外发现为零”,请添加[弱自我]
在之前(数据、响应、错误)
,请重新检查我的答案:)你能告诉我你面对的是哪一行错误吗?@ShaniceC.Oops!对不起,错过了那一部分。我试过了,仍然显示相同的错误。这里的屏幕截图供你参考:我想你的tableView
是nil
请检查你的tableView
的插座连接。你能告诉我哪个专业perty是nil
?self
和tableView
在这种情况下可以是nil
。您可以在控制台左侧轻松找到这一点。我已将其更改为row,运行时没有任何错误,但也没有显示任何内容。我猜这是因为我在getTokenFromAPI()中附加了tempArray未在函数外“保存”。因此它仍然为空。但感谢您在下标上指出!除了此修复之外,请按照@emrcftci在其回答中的建议执行,因为当数据更改时,应重新加载tableView。这在闭包中发生。此外,您的tempArray
是Da的属性shboardViewControlle