如何等待Swift的URLSession完成再重新运行?

时间:2018-08-05 06:27:33

标签: swift firebase nsurlsession google-books

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

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

幸运的是,我可以指定从何处开始索引并获取接下来要搜索的40本书。不幸的是,我已经尝试了几个小时来了解URLSession的工作原理。我尝试过的所有方法都向我展示了URLSession块之后的代码不必等待会话完成。因此,如果我检查之后是否找到任何匹配项,甚至可能无法完成搜索。

我怀疑答案是在完成处理中,但是到目前为止,我的尝试均未成功。下面是我的代码,其中包含使用各种起始索引值的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)
    }

2 个答案:

答案 0 :(得分:0)

将一个bool变量声明为isLoading,如果该函数正在加载,则不要触发urlsession。希望下面的示例对您有帮助。

0x6D, 0x00

答案 1 :(得分:0)

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

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将位于该块中。)

在处理完第一个结果之前,不要启动下一个搜索,这样就避免了必须处理后续回调的任何问题,这些回调试图与上一个查询同时更新数据。

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

HTH