[英]iOS swift unit testing - How to test function that makes an api call?
[英]How to call an instance of a class that makes an API call, and a function within that class that makes the request, and assign this to variable? Swift
这是对以下问题的跟进: 为什么代码没有在第二个 URLSession.shared.dataTask 之后/在第二个 URLSession.shared.dataTask 之后执行,即在初始 URLSession.shared.dataTask 的 do 块内? 迅速
我正在尝试使用self.variable
将变量的当前实例分配给类实例的函数调用。
这可以在附加代码的 ViewController.swift 中以“self.venues =”开头的代码行中看到。
我相信Task
与此有关。 我已经阅读了有关Task
的文档,并在网上阅读了更多关于它的信息,但还没有找到解决方案。
另外:我在 ViewController.swift 中以“self.venues =”开头的代码行中收到错误消息“Cannot assign value of type 'Task<(), Never>' to type '[Venue]'”。
代码:
ViewController.swift
:
import UIKit
import CoreLocation
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet var tableView: UITableView!
var venues: [Venue] = []
override func viewDidLoad() async {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
tableView.delegate = self
tableView.dataSource = self
let yelpApi = YelpApi(apiKey: "Api key")
self.venues = Task {
do { try await yelpApi.searchBusiness(latitude: selectedLatitude, longitude: selectedLongitude, category: "category quary goes here", sortBy: "sort by quary goes here", openAt: )
}
catch {
//Handle error here.
print("Error")
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
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 UIKit
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
}
}
谢谢!
您有一个异步操作 - searchBusinesses
。 调用此函数时,需要时间才能得到结果。 您正在使用await
来处理此问题。
您不能在异步上下文之外使用await
,而viewDidLoad
不是。 您正在使用Task
来创建异步上下文。 到目前为止,一切都很好。
你出错的地方是试图将结果分配给venues
。 您只能在await
完成后执行此分配。 您不会从Task
获得此结果,而是在Task
中获得它:
Task {
do {
self.venues = try await yelpApi.searchBusiness(latitude: selectedLatitude, longitude: selectedLongitude, category: "category quary goes here", sortBy: "sort by quary goes here", openAt: )
self.tableView.reloadData()
} catch {
//Handle error here.
print("Error")
}
}
请注意,您不应该将 GCD 调度队列和 async/await 结合使用,在这种情况下,您无需担心主队列。
UIViewController
被标记为@MainActor
。 这意味着除非您专门创建分离任务,否则任务已经在主要参与者上执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.