如何调用发出 API 调用的 class 的实例,以及发出请求的 class 中的函数,并将其分配给变量? Swift

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

这是对以下问题的跟进:

我正在尝试将使用 self.variable 的变量的当前实例分配给来自 class.

实例的函数调用

这可以在以下代码行中看到:“self.venues =” in ViewController.swift in the attached code.

我相信 Task 与此有关。我已经阅读了有关 Task 的文档,并在网上阅读了更多相关信息,但尚未找到解决方案。

此外:我收到错误消息“无法将类型 'Task<(), Never>' 的值分配给类型 '[Venue]'”,代码行开头为:“self.venues = “ in [=46] =].

代码:

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。这意味着任务已经在主要参与者上执行,除非您专门创建一个分离任务。