swift - 如何在再次运行之前等待 Swift 的 URLSession 完成?
问题描述
可能是一个愚蠢的问题,但我是这方面的初学者。
下面的代码应该通过关键字搜索从 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)
}
解决方案
将 bool 变量声明为 isLoading,如果该函数正在加载,则不要触发 urlsession。希望下面的示例对您有所帮助。
var isLoading : Bool = false
func loadMore(with pageCount: Int){
if isLoading { return }
isLoading = true
// call the network
URLSession.shared.dataTask(with: URL(string: "xxxxx")!) { (data, response, error) in
// after updating the data set isloding to false again
// do the api logic here
//
DispatchQueue.main.async {
// self.items = downloadedItems
self.tableView.reloadData()
self.isLoading = false
}
}.resume()
}
推荐阅读
- javascript - 更改路由更改 Next.js 的状态
- ssl-certificate - 如何通过 SSL 启用仅限 Intranet 的站点以进行设备访问?
- c# - WPF 文本块文本不会在组合框选定项上动态更改
- azure - 更改 Azure Pipelines 中的当前工作目录
- python - 关于在递归中使用全局变量或局部变量
- sql-server - 使用带有分页的 CTE 从 3 个表中获取记录
- assembly - IA32 Assembly - 如何创建 1 到 10 之间的两个随机数?
- eclipse - Eclipse C/C++ - MercurialEclipse 未列出安装
- uwp-xaml - NavigationViewItem 问题的内容属性
- c# - 通过 JSON.NET 反序列化 Elasticsearch 结果