swift - 如何等待 Swift 的 URLSession 完成后再运行?

标签 swift firebase nsurlsession google-books

可能是一个愚蠢的问题,但我是这方面的初学者。

下面的代码应该通过关键字搜索从 Google 图书获取图书信息。然后它会检查结果并检查 Firebase 数据库中是否有匹配的 ISBN。它可以工作,但目前只能搜索 40 本书,因为这是 Google Books API 每次搜索的上限。

幸运的是,我可以指定从哪里开始索引并获取接下来的 40 本书进行搜索。不幸的是,我已经尝试了几个小时来了解 URLSession 的工作原理。我尝试过的所有方法都表明 URLSession block 之后的代码不一定要等待 session 完成。因此,如果我随后检查是否找到任何匹配项,则可能根本没有完成搜索。

我怀疑答案在于完成处理,但到目前为止我的尝试尚未成功。下面是我的代码,其中包含一个 URL 设置,用于获取各种起始索引值。

var startingIndex = 0

        //encode keyword(s) to be appended to URL
        let query = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        let url = "https://www.googleapis.com/books/v1/volumes?q=\(query)&&maxResults=40&startIndex=\(startingIndex)"

        URLSession.shared.dataTask(with: URL(string: url)!) { (data, response, error) in
            if error != nil {
                print(error!.localizedDescription)
            }else{

                let json = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String: AnyObject]

                if let items = json["items"] as? [[String: AnyObject]] {

                    //for each result make a book and add title
                    for item in items {
                        if let volumeInfo = item["volumeInfo"] as? [String: AnyObject] {
                            let book = Book()
                            //default values
                            book.isbn13 = "isbn13"
                            book.isbn10 = "isbn10"
                            book.title = volumeInfo["title"] as? String

                            //putting all authors into one string
                            if let temp = volumeInfo["authors"] as? [String] {
                                var authors = ""
                                for i in 0..<temp.count {
                                    authors = authors + temp[i]
                                }
                                book.author = authors
                            }

                            if let imageLinks = volumeInfo["imageLinks"] as? [String: String] {
                                book.imageURL = imageLinks["thumbnail"]
                            }

                            //assign isbns
                            if let isbns = volumeInfo["industryIdentifiers"] as? [[String: String]] {

                                for i in 0..<isbns.count {

                                    let firstIsbn = isbns[i]
                                    if firstIsbn["type"] == "ISBN_10" {
                                        book.isbn10 = firstIsbn["identifier"]
                                    }else{
                                        book.isbn13 = firstIsbn["identifier"]
                                    }
                                }
                            }

                            //adding book to an array of books
                            myDatabase.child("listings").child(book.isbn13!).observeSingleEvent(of: .value, with: { (snapshot) in
                                if snapshot.exists() {
                                    if listings.contains(book) == false{
                                        listings.append(book)
                                    }
                                    DispatchQueue.main.async { self.tableView.reloadData() }
                                }
                            })
                            myDatabase.child("listings").child(book.isbn10!).observeSingleEvent(of: .value, with: { (snapshot) in
                                if snapshot.exists() {
                                    if listings.contains(book) == false{
                                        listings.append(book)
                                    }
                                    DispatchQueue.main.async { self.tableView.reloadData() }
                                }
                            })
                        }
                    }
                }
            }

            SVProgressHUD.dismiss()
            }.resume()

下面是我修改后的代码:

 func searchForSale(query: String, startingIndex: Int) {

        directionsTextLabel.isHidden = true
        tableView.isHidden = false
        listings.removeAll()
        DispatchQueue.main.async { self.tableView.reloadData() }
        SVProgressHUD.show(withStatus: "Searching")

        //clear previous caches of textbook images
        cache.clearMemoryCache()
        cache.clearDiskCache()
        cache.cleanExpiredDiskCache()


        let url = "https://www.googleapis.com/books/v1/volumes?q=\(query)&&maxResults=40&startIndex=\(startingIndex)"

        URLSession.shared.dataTask(with: URL(string: url)!) { (data, response, error) in
            if error != nil {
                print(error!.localizedDescription)
            }else{

                var needToContinueSearch = true

                let json = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String: AnyObject]

                if json["error"] == nil {

                    let totalItems = json["totalItems"] as? Int
                    if totalItems == 0 {
                        SVProgressHUD.showError(withStatus: "No matches found")
                        return
                    }

                    if let items = json["items"] as? [[String: AnyObject]] {

                        //for each result make a book and add title
                        for item in items {

                            if let volumeInfo = item["volumeInfo"] as? [String: AnyObject] {

                                let book = Book()
                                //default values
                                book.isbn13 = "isbn13"
                                book.isbn10 = "isbn10"
                                book.title = volumeInfo["title"] as? String

                                //putting all authors into one string
                                if let temp = volumeInfo["authors"] as? [String] {
                                    var authors = ""
                                    for i in 0..<temp.count {
                                        authors = authors + temp[i]
                                    }
                                    book.author = authors
                                }

                                if let imageLinks = volumeInfo["imageLinks"] as? [String: String] {
                                    book.imageURL = imageLinks["thumbnail"]
                                }

                                //assign isbns
                                if let isbns = volumeInfo["industryIdentifiers"] as? [[String: String]] {

                                    for i in 0..<isbns.count {

                                        let firstIsbn = isbns[i]
                                        //checks if isbns have invalid characters
                                        let isImproperlyFormatted = firstIsbn["identifier"]!.contains {".$#[]/".contains($0)}

                                        if isImproperlyFormatted == false {
                                            if firstIsbn["type"] == "ISBN_10" {
                                                book.isbn10 = firstIsbn["identifier"]
                                            }else{
                                                book.isbn13 = firstIsbn["identifier"]
                                            }
                                        }
                                    }
                                }

                                //adding book to an array of books
                                myDatabase.child("listings").child(book.isbn13!).observeSingleEvent(of: .value, with: { (snapshot) in
                                    if snapshot.exists() {
                                        if listings.contains(book) == false{
                                            listings.append(book)
                                            needToContinueSearch = false
                                        }
                                        DispatchQueue.main.async { self.tableView.reloadData() }
                                    }
                                })
                                myDatabase.child("listings").child(book.isbn10!).observeSingleEvent(of: .value, with: { (snapshot) in
                                    if snapshot.exists() {
                                        if listings.contains(book) == false{
                                            listings.append(book)
                                            needToContinueSearch = false
                                        }
                                        DispatchQueue.main.async { self.tableView.reloadData() }
                                        return
                                    }
                                    if startingIndex < 500 {
                                        if needToContinueSearch {
                                            let nextIndex = startingIndex + 40
                                            self.searchForSale(query: query, startingIndex: nextIndex)
                                        }
                                    }
                                })
                            }
                        }
                    }
                }else{
                    return
                }
            }

            SVProgressHUD.dismiss()
            }.resume()

        //hide keyboard
        self.searchBar.endEditing(true)
    }

最佳答案

在完成处理程序中,如果返回任何结果,则结束:

DispatchQueue.main.async { self.tableView.reloadData() }

触发使用更新的信息重新加载表。在同一点上,您可以确定可能有更多结果并启动下一个异步 URL 任务。概括地说,您的代码可能是:

let needToContinueSearch : Bool = ...;

DispatchQueue.main.async { self.tableView.reloadData() }

if needToContinueSearch
{  // call routine it initiate next async URL task
}

(如果有任何原因从主线程启动任务,if 将位于 block 中。)

通过在处理完第一个搜索结果之后才启动下一个搜索,您可以避免处理后续回调尝试与前一个搜索同时更新数据的任何问题。

但是,如果您发现以这种方式延迟第二次搜索太慢,您可以研究重叠操作的方法,例如您可能会让回调将结果的处理传递给串行队列上的异步任务(以便一次只处理一组结果)并启动下一个异步 URL 任务。

HTH

关于swift - 如何等待 Swift 的 URLSession 完成后再运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51691943/

相关文章:

ios - 为 View 中的所有标签添加边框

swift - 简单的 NSPageController 示例抛出未知 subview 警告并停止工作

swift - 如何获取用户个人资料中的帖子? - swift 和火力地堡

ios - 使用 Alamofire 上传 MultipartFormData

swift - NSURLSession 参数无法识别

ios - NSURLSessionAuthChallengeUseCredential 没有帮助。如何让 iOS 信任我的服务器?

ios - 如何在 SpriteKit 中获得类似于 peek 或 pop 的 3d touch 应用程序图标触觉反馈

arrays - 更新字典数组中的值

android - Android 和 iOS 之间关于数据消息的 FCM 差异

node.js - 为什么 req.cookies.session 未定义? Firebase + Node + Express