这是以下问题的后续:如何调用进行 API 调用的类的实例以及该类中发出请求的函数,并将其分配给变量?迅速 https://stackoverflow.com/questions/72400226/how-to-call-an-instance-of-a-class-that-makes-an-api-call-and-a-function-within.
我试图将一个新的视图控制器推入堆栈,并从初始视图控制器中呈现此视图控制器,但是当我运行程序时,应该在新视图控制器中显示的表视图不会加载在模拟器上。在运行程序之前、期间或之后,我根本没有收到来自 Xcode 的任何错误通知。此外,代码似乎没有在这个新视图控制器中执行,因为新视图控制器类(和视图控制器 .swift 文件)顶部/最开头的打印语句没有被打印。
当应该显示新视图控制器中的新表视图时,显示的是空白屏幕,但导航栏仍在顶部,后退按钮位于导航栏的左上角(就像通常一样)在更改为使用 YelpApi 类进行 API 请求并使用 async/await 之前正确显示表视图时)。发生这种情况时,我也没有在终端中收到任何错误消息。
我认为与该问题相关的是新的 YelpApi 类,该类用于在此处发出 API 端点请求,并使用 async/await。直到我使用这个新类和 async/await 重构代码后才出现这个问题。
我认为更具体地说,可能导致问题的是我在 NewViewController.swift 中的“func viewDidLoad() async {”之前删除了“覆盖”。我这样做是因为我将其留在那里时遇到错误,并找到了建议将其取出的解决方案,但是,正如已接受答案的评论中提到的那样,执行此操作存在问题(问题是没有编译时检查以确保您的签名正确):Swift 协议:方法不会覆盖其超类中的任何方法 https://stackoverflow.com/questions/24380681/swift-protocols-method-does-not-override-any-method-from-its-superclass.
我已经在网上(包括此处)查看过这个问题(表格视图未显示),但找不到可行的解决方案。一篇类似的帖子是这样的:视图控制器未正确显示 https://stackoverflow.com/questions/38782900/view-controller-not-showing-properly但我的代码已经以与接受的答案类似且相同的形式设置。我还让程序运行了 20 分钟,然后退出程序,以防请求由于某种原因花费了很长时间,但是仍然没有呈现所需的表格视图。
Code:
InitialViewController.swift
:
//*Code for creating a table view that shows options to the user, for the user to select.*
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//*Code for assigning values to variables related to what row in the table view the user selected.*
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let newVC = storyboard.instantiateViewController(identifier: "NewViewController") as! NewViewController
newVC.modalPresentationStyle = .fullScreen
newVC.modalTransitionStyle = .crossDissolve
//Print Check.
//Prints.
print("Print Check: Right before code for presenting the new view controller.")
navigationController?.pushViewController(newVC, animated: true)
}
NewViewController.swift
import UIKit
import CoreLocation
class NewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//Print Check.
//Doesn't print.
func printCheckBeforeIBOutletTableViewCode() {
print("Print Check: Right before tableView IBOutlet code at top of NewViewController.swift file.")
}
@IBOutlet var tableView: UITableView!
var venues: [Venue] = []
//Print Check.
//Doesn't print.
func printCheckAfterIBOutletTableViewCode() {
print("Print Check: Right after tableView IBOutlet code at top of NewViewController.swift file.")
}
func viewDidLoad() async {
super.viewDidLoad()
//Function calls for print checks.
//Doesn't print.
self.printCheckBeforeIBOutletTableViewCode()
self.printCheckAfterIBOutletTableViewCode()
tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
tableView.delegate = self
tableView.dataSource = self
//Print Check.
//Doesn't print.
print("Print Check: Right before creating an instance of YelpApi class, then creating a task to make the API request.")
let yelpApi = YelpApi(apiKey: "Api key")
Task {
do {
self.venues = try await yelpApi.searchBusiness(latitude: selectedLatitude, longitude: selectedLongitude, category: "category query goes here", sortBy: "sort by query goes here")
self.tableView.reloadData()
} catch {
//Handle error here.
print("Error")
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return venues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
//Details for custom table view cell go here.
}
//Rest of table view protocol functions.
}
Venue.swift
:
import Foundation
// MARK: - BusinessSearchResult
struct BusinessSearchResult: Codable {
let total: Int
let businesses: [Venue]
let region: Region
}
// MARK: - Business
struct Venue: Codable {
let rating: Double
let price, phone, alias: String?
let id: String
let isClosed: Bool?
let categories: [Category]
let reviewCount: Int?
let name: String
let url: String?
let coordinates: Center
let imageURL: String?
let location: Location
let distance: Double
let transactions: [String]
enum CodingKeys: String, CodingKey {
case rating, price, phone, id, alias
case isClosed
case categories
case reviewCount
case name, url, coordinates
case imageURL
case location, distance, transactions
}
}
// MARK: - Category
struct Category: Codable {
let alias, title: String
}
// MARK: - Center
struct Center: Codable {
let latitude, longitude: Double
}
// MARK: - Location
struct Location: Codable {
let city, country, address2, address3: String?
let state, address1, zipCode: String?
enum CodingKeys: String, CodingKey {
case city, country, address2, address3, state, address1
case zipCode
}
}
// MARK: - Region
struct Region: Codable {
let center: Center
}
FetchData.swift
:
import Foundation
import CoreLocation
class YelpApi {
private var apiKey: String
init(apiKey: String) {
self.apiKey = apiKey
}
func searchBusiness(latitude: Double,
longitude: Double,
category: String,
sortBy: String) async throws -> [Venue] {
var queryItems = [URLQueryItem]()
queryItems.append(URLQueryItem(name:"latitude",value:"\(latitude)"))
queryItems.append(URLQueryItem(name:"longitude",value:"\(longitude)"))
queryItems.append(URLQueryItem(name:"categories", value:category))
queryItems.append(URLQueryItem(name:"sort_by",value:sortBy))
var results = [Venue]()
var expectedCount = 0
let countLimit = 50
var offset = 0
queryItems.append(URLQueryItem(name:"limit", value:"\(countLimit)"))
repeat {
var offsetQueryItems = queryItems
offsetQueryItems.append(URLQueryItem(name:"offset",value: "\(offset)"))
var urlComponents = URLComponents(string: "https://api.yelp.com/v3/businesses/search")
urlComponents?.queryItems = offsetQueryItems
guard let url = urlComponents?.url else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.setValue("Bearer \(self.apiKey)", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let businessResults = try JSONDecoder().decode(BusinessSearchResult.self, from:data)
expectedCount = min(businessResults.total,1000)
results.append(contentsOf: businessResults.businesses)
offset += businessResults.businesses.count
} while (results.count < expectedCount)
return results
}
}
Thanks!
Update:
根据 Andreas 和 Paulw11 的建议进行更改后,程序运行时表视图仍然没有加载,并且我在终端中得到的最后一个打印语句是“错误”catch
块内Task
创建 YelpApi 类的实例以发出初始 API 请求后NewViewController.swift
。此后,我已将文件中的“错误”语句更改为“发出初始 API 端点请求时发生错误”。为了清楚起见。
以下是更新版本NewViewController.swift
根据 Andreas 和 Paulw11 建议的更改,以及FetchData.swift
带有新的打印语句以帮助识别新问题。这些更新版本下面是进行 Andreas 和 Paulw11 建议的更改后来自终端的返回语句,其中还包含 FetchData.swift 更新版本中的新打印语句(用于帮助识别新问题)。
更新版本NewViewController.swift
with override
插入并async
带走:
import UIKit
import CoreLocation
class NewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//Print Check.
//Prints.
func printCheckBeforeIBOutletTableViewCode() {
print("Print Check: Right before tableView IBOutlet code at top of NewViewController.swift file.")
}
@IBOutlet var tableView: UITableView!
var venues: [Venue] = []
//Print Check.
//Prints.
func printCheckAfterIBOutletTableViewCode() {
print("Print Check: Right after tableView IBOutlet code at top of NewViewController.swift file.")
}
override func viewDidLoad() {
super.viewDidLoad()
//Function calls for print checks.
//Prints.
self.printCheckBeforeIBOutletTableViewCode()
self.printCheckAfterIBOutletTableViewCode()
tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
tableView.delegate = self
tableView.dataSource = self
//Print Check.
//Prints.
print("Print Check: Right before creating an instance of YelpApi class, then creating a task to make the API request.")
let yelpApi = YelpApi(apiKey: "Api key")
Task {
do {
self.venues = try await yelpApi.searchBusiness(latitude: selectedLatitude, longitude: selectedLongitude, category: "category query goes here", sortBy: "sort by query goes here")
self.tableView.reloadData()
} catch {
//Handle error here.
//Prints.
print("Error")
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return venues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
//Details for custom table view cell go here.
}
//Rest of table view protocol functions.
}
更新版本FetchData.swift
其中包括新的打印语句以帮助识别新问题:
import Foundation
import CoreLocation
class YelpApi {
private var apiKey: String
init(apiKey: String) {
self.apiKey = apiKey
}
func searchBusiness(latitude: Double,
longitude: Double,
category: String,
sortBy: String) async throws -> [Venue] {
var queryItems = [URLQueryItem]()
queryItems.append(URLQueryItem(name:"latitude",value:"\(latitude)"))
queryItems.append(URLQueryItem(name:"longitude",value:"\(longitude)"))
queryItems.append(URLQueryItem(name:"categories", value:category))
queryItems.append(URLQueryItem(name:"sort_by",value:sortBy))
var results = [Venue]()
var expectedCount = 0
let countLimit = 50
var offset = 0
queryItems.append(URLQueryItem(name:"limit", value:"\(countLimit)"))
//Print Check.
//Prints.
print("Print Check: Line before repeat-while loop.")
repeat {
//Print Check.
//Prints.
print("Print Check: Within repeat-while loop and before first line of code within it.")
var offsetQueryItems = queryItems
offsetQueryItems.append(URLQueryItem(name:"offset",value: "\(offset)"))
var urlComponents = URLComponents(string: "https://api.yelp.com/v3/businesses/search")
urlComponents?.queryItems = offsetQueryItems
guard let url = urlComponents?.url else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.setValue("Bearer \(self.apiKey)", forHTTPHeaderField: "Authorization")
//Print Check.
//Prints.
print("Print Check: Within repeat-while loop and before 'let (data, _) = try await' line of code.")
let (data, _) = try await URLSession.shared.data(for: request)
//Print Check.
//Prints.
print("Print Check: Within repeat-while loop and before 'let businessResults = try JSONDecoder()' line of code.")
let businessResults = try JSONDecoder().decode(BusinessSearchResult.self, from:data)
//Print Check.
//Doesn't print.
print("Print Check: Within repeat-while loop and right after 'let businessResults = try JSONDecoder()' line of code.")
expectedCount = min(businessResults.total,1000)
results.append(contentsOf: businessResults.businesses)
offset += businessResults.businesses.count
} while (results.count < expectedCount)
//Print Check.
//Doesn't print.
print("Print Check: After repeat-while loop and before 'return results' code.")
return results
}
}
Returned Print Statements from Terminal Returned After Making Andreas and Paulw11's Suggested Changes and Running Program
:
Print Check: Right before code for presenting the new view controller.
Print Check: Right before tableView IBOutlet code at top of NewViewController.swift file.
Print Check: Right after tableView IBOutlet code at top of NewViewController.swift file.
Print Check: Right before creating an instance of YelpApi class, then creating a task to make the API request.
Print Check: Line before repeat-while loop.
Print Check: Within repeat-while loop and before first line of code within it.
Print Check: Within repeat-while loop and before 'let (data, _) = try await' line of code.
Date and Time, Project Name, and some other info [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
Print Check: Within repeat-while loop and before 'let businessResults = try JSONDecoder()' line of code.
Error occurred when making initial API endpoint request.
必须退出程序,因为它在终端中执行上面最后一条打印语句后“停滞”/没有执行任何操作。
此外,每当我发出 API 请求时,都会出现“日期和时间、项目名称和其他一些信息 [boringssl] Boringssl_metrics_log_metric_block_invoke(153) 无法记录指标”打印语句,并且经过研究,不会对项目。
在“发出初始 API 端点请求时发生错误”之前打印的最后一个打印语句。是“打印检查:在repeat-while循环内和‘letbusinessResults = try JSONDecoder()’代码行之前。”表明代码行出了问题let businessResults = try JSONDecoder().decode(BusinessSearchResult.self, from:data)
,这让我认为问题可能正在发生并且与附加时有关queryItems
for category
and sort_by
in the searchBusinesses
的功能YelpApi
类定义在FetchData.swift
。我正在努力解决这个问题,如果我解决了这个问题,我会更新。
感谢您的帮助!