swift 如何使用合并向UIViewController发送API响应

mgdq6dx1  于 2023-01-08  发布在  Swift
关注(0)|答案(1)|浏览(120)

我正在使用合并使用我的API的响应更新我的collectionView,以提供真实的信息。我的API返回工作正常的NSArray,但由于SearchAPI类中的一些奇怪原因,我可以接收响应并在控制台中打印出来**打印(“我们的数组“,searchArray)**但无法接收到我的UIViewController并相应地更新我的collectionView。**print(“value“,value)**Value始终为空

import Foundation
import Combine

class SearchAPI {
    static let shared = SearchAPI()
    
    func fetchData(url: String, category: String, queryString: String) -> Future<NSArray, Error>{
            var searchArray: NSArray = []
            let urlString = url
            print("url come ", urlString)
            guard let url = URL(string: urlString) else {
                fatalError()
            }
            var request = URLRequest(url: url)
            request.setValue("application/json", forHTTPHeaderField: "Accept")
            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            request.httpMethod = "GET"
            
            let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
                guard let data = data, error == nil else {
                    return
                }
                
                do{
                    let json = try? JSONSerialization.jsonObject(with: data, options: []) as? NSArray
                    if let responseJson = json {
                        searchArray = responseJson
                        print("Our array", searchArray)
                    }
                }
            }
            
            task.resume()
            return Future { promixe in
                DispatchQueue.main.async {
                    promixe(.success(searchArray))
                }
            }
        }
}

//In my UIViewController

    var observers: [AnyCancellable] = []
    let action = PassthroughSubject<NSArray, Never>()
    var category = "tv"
    var queryString = ""
    private var models: NSArray = []
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if isMovieSelected {
            btnMoviesBottomBorder.backgroundColor = .secondaryPink
            btnAlbumsBottomBorder.backgroundColor = .systemGray
            btnAlbumsBottomBorder.backgroundColor = .systemGray
        }
        txtSearch.searchTextField.addTarget(self, action: #selector(searchItem), for: .editingChanged)
    }
    @objc func searchItem(){
        moviesView.alpha = 0
        albumsView.alpha = 0
        booksView.alpha = 0
        lblSrchResults.alpha = 1
        queryString = txtSearch.text!.replacingOccurrences(of: " ", with: "%20")
        print("who is calling ", queryString)
        let url = "https://endpoint?category=\(category)&query=\(queryString)"
        SearchAPI.shared.fetchData(url: url, category: category, queryString: queryString)
                    .receive(on: DispatchQueue.main)
                    .sink(receiveCompletion: { completion in
                        switch completion {
                        case .finished:
                            print("finished")
                        case .failure(let error):
                            print(error)
                        }
                }, receiveValue: { [weak self] value in
                    print("value ", value)
                    self?.models = value
                    self?.searchCollectionView!.reloadData()
                }).store(in: &observers)
        
        
    }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        print("Fetched ", models.count)
        return models.count
    }
vhipe2zx

vhipe2zx1#

fetchData函数中,创建一个dataTask来运行URL请求,在稍后的某个时刻,dataTask将完成并返回一个数组。
然后创建一个Future,并发送一些代码在主队列上运行,这些代码中没有任何内容可以让它等待dataTask完成。
Future中的代码将“立即”运行,并在为searchArray赋值之前完成。
你需要修改你的Future,使它只在dataTask完成后才解析(只完成)。稍微调整一下你的代码,它看起来像这样:

func fetchData(url: String, category: String, queryString: String) -> Future<NSArray, Error>
    {
        return Future { promixe in
            var searchArray: NSArray = []
            let urlString = url
            print("url come ", urlString)
            guard let url = URL(string: urlString) else {
                fatalError()
            }
            var request = URLRequest(url: url)
            request.setValue("application/json", forHTTPHeaderField: "Accept")
            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            request.httpMethod = "GET"
            
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                guard let data,
                      error == nil else {
                    promixe(.failure(error!))
                    return
                }
                
                do{
                    let json = try? JSONSerialization.jsonObject(with: data, options: []) as? NSArray
                    if let responseJson = json {
                        searchArray = responseJson
                        print("Our array", searchArray)
                        
                        promixe(.success(searchArray))
                    }
                }
            }
            
            task.resume()
        }
    }

我把它输入了一个操场,但我没有运行它,所以可能需要额外的更改。
现在所有的动作都发生在未来,一旦你在未来块中,你就应该调用它的resolution函数(在你的代码中叫做promixe)来处理所有成功或失败的情况。
我修改了代码,以便仅在数据任务完成(成功或不成功)时调用promixe
对于您的代码,您可能还应该:

  • 请确保在通过Futures块的每个路径上调用proximate一次。特别是,您需要捕获JSONSerialization可能给您带来的错误,并调用promixe报告错误。否则,如果数据从服务器返回,但无法解析,您的未来可能无法完成。
  • 更改代码以使用Swift风格的JSON,而不是使用JSONSerialization,这样您将使用[SomeType]而不是NSArray。使用类型系统以减少错误的可能性。
  • 使用txtSearch.text!.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)代替queryString = txtSearch.text!.replacingOccurrences(of: " ", with: "%20")。如果用户输入一些意外的内容,它将涵盖更多的情况。

相关问题