Ios 如何在Swift中构造多个HTTP请求?
我对Swift和编程完全是新手。我非常热衷于学习所有正确的方法。因此,任何额外的提示或评论都是非常感谢的 我正在对api执行HTTP请求,这很好。问题是,每个请求只能得到100个结果。我可以设置一个可选的偏移量和限制。如果我将限制设置为101,则会出现一个服务器错误:“请求错误:为限制指定的值无效。允许的最大值为100。”总数为101,因此我需要至少执行两个请求。只有在收到所有请求的总数据后,我才想填充我的tableview。这就是我所拥有的:Ios 如何在Swift中构造多个HTTP请求?,ios,swift,httprequest,Ios,Swift,Httprequest,我对Swift和编程完全是新手。我非常热衷于学习所有正确的方法。因此,任何额外的提示或评论都是非常感谢的 我正在对api执行HTTP请求,这很好。问题是,每个请求只能得到100个结果。我可以设置一个可选的偏移量和限制。如果我将限制设置为101,则会出现一个服务器错误:“请求错误:为限制指定的值无效。允许的最大值为100。”总数为101,因此我需要至少执行两个请求。只有在收到所有请求的总数据后,我才想填充我的tableview。这就是我所拥有的: class Book { var id: Int
class Book {
var id: Int
var title: String
let description: String
var coverImage: String
var isbn: String
var publisherID: Int
var publisherName: String
var authorID: Int
var authorFirstName: String
var authorLastName: String
class func getDataFromJson(completionHandler: ([Book]) -> ()) {
var books = [Book]()
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSURLRequest(URL: NSURL(string: "http://example.website.nl/books/highlighted")!)
let task: NSURLSessionDataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if let data = data {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
if let booksFromResult = json["books"] as? [[String: AnyObject]] {
for book in booksFromResult {
let bookID = book["id"] as! Int
let bookTitle = book["title"] as! String
let bookDescription = book["description"] as! String
let bookCoverImage = book["cover_url"] as! String
let bookISBN = book["isbn"] as! String
if let bookPublisher = book["publisher"] as? [String: AnyObject] {
let bookPublisherID = bookPublisher["id"] as! Int
let bookPublisherName = bookPublisher["name"] as! String
if let bookAuthor = book["author"] as? [String: AnyObject] {
let bookAuthorID = bookAuthor["id"] as! Int
let bookAuthorFirstname = bookAuthor["first_name"] as! String
let bookAuthorLastName = bookAuthor["last_name"] as! String
books.append(Book(id: bookID, title: bookTitle, description: bookDescription, coverImage: bookCoverImage, isbn: bookISBN, publisherID: bookPublisherID, publisherName: bookPublisherName, authorID: bookAuthorID, authorFirstName: bookAuthorFirstname, authorLastName: bookAuthorLastName))
}
}
}
print(books.count)
}
dispatch_async(dispatch_get_main_queue(),{
completionHandler(books)
})
} catch {
print("error serializing JSON: \(error)")
}
}
}
task.resume()
}
init(id: Int, title: String, description: String, coverImage: String, isbn: String, publisherID: Int, publisherName: String, authorID: Int, authorFirstName: String, authorLastName: String) {
self.id = id
self.title = title
self.description = description
self.coverImage = coverImage
self.isbn = isbn
self.publisherID = publisherID
self.publisherName = publisherName
self.authorID = authorID
self.authorFirstName = authorFirstName
self.authorLastName = authorLastName
}
}
我已经尝试解决这个问题超过24小时了。我真的在这里和网上搜索了一个例子。我在这里找到的小东西帮不了我
我的想法是如何做到这一点:
你说“问题是每个请求只能有50个”,但不清楚这是什么意思。是什么将响应限制为50项?服务器代码?那你怎么申请下一批50件呢?是否有一个确定的协议来确定一个请求总共有多少项,然后请求50个结果的批次?嗨,Duncan,很抱歉不清楚,他们称之为可选的偏移量和限制。当我收到这批货时,我看到限值是50,还有一个总值是101。@Duncan,你让我用另一种方式思考。我可能不明白这个解释。上限是100,但我需要拿回101。如果我将限制设置为101,则会出现一个服务器错误:“请求错误:为
限制指定的值无效。允许的最大值为100。”好的,您需要编辑问题以显示附加信息。听起来您需要询问服务器他们总共有多少个条目,然后如果结果>50,编写代码一次循环请求50个条目,然后每次收到前50个条目时发送一个新的请求。@Duncan,我编辑了这个问题,谢谢。我已经找回了所有的条目,也就是101条。我确实理解一个接一个地处理请求的逻辑。我不明白的是如何去做。“谢谢!”汤米,非常感谢你。我确实计划重新排序,并使用结构和枚举来处理错误。肯定会用守卫让别人。我会研究你的代码,并尝试理解它的作用,谢谢。通过经历,有一件事我不确定。我需要o三个不同的URL请求。我必须更改第二个和第三个请求中的偏移量,以获得正确的批次。在您提供的代码中,我似乎找不到这个选项。也许我理解得不够好?或者我的解释不够清楚?我不知道你必须更改每个请求的url。我会在一分钟内提交答案。是的,对不起,那不清楚。谢谢你的帮助,你太棒了!你是一个灵感!!非常感谢你!这会让我忙上好一阵子,同时试着去了解发生了什么。谢谢你的评论,这很有帮助!我想投票支持你的答案,但我似乎缺乏声誉:-(。我快到了,我还需要4个。一旦我有了他们,我会投票。哈哈,没问题,如果你需要任何进一步的解释,尽管问。我会警告你,这只是由编译器检查,我没有运行任何测试,以确保每个部分在运行时工作(即json,这是您的职责)。但是如果您遇到任何奇怪的编译问题,请发表评论。
// Heavily based on the video I recommended. Watch it for a great explanation
struct Resource<A>{
let url: NSURL
let parse: (NSData) -> [A]?
}
extension Book {
// You could figure a way to dynamically populate this based on limiting
static let urls = [NSURL(string: "http://example.website.nl/books/highlighted")!,
NSURL(string: "http://example.website.nl/books/highlighted2")!]
// Creates an array of Requests passing in each url for the url, but the same parse function
static let requests = urls.map { Resource<Book>(url: $0, parse: Book.parse) }
// Used by Webservice (from the Resource struct) to parse the data into a Book
static let parse: (NSData?) -> [Book]? = { data in
guard let data = data else { return nil }
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) else {
print("Error deserializing json.")
return nil
}
var books: [Book]? = nil
guard let jsonBooks = json["books"] as? [[String: AnyObject]] else { return nil }
for jsonBook in jsonBooks {
guard let book = Book(fromJson: jsonBook) else { continue } // skips nil books from failable initializer, depends on how you want to handle that
books = books ?? [Book]() // if nil create a new array, if not use the old one
books!.append(book)
}
return books
}
}
class Webservice {
// A stands for a generic type. You could add a type called Publisher later and use the same function
// This adopted from the video I showed you so it's a little more in depth
func loadAll<A>(resources: [Resource<A>], completion: [A] -> ()) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var currentRequest = 0 // used to keep track of asynchronous callback order
var allA = [A]()
for resource in resources {
session.dataTaskWithURL(resource.url) { (data, _, _) in
defer {
currentRequest += 1 // marks that we're done with one request
// This check ensures that we only call the completion handler
// after we're done with the last request
if currentRequest == resources.count {
completion(allA)
}
}
guard let data = data else { return }
// this parse function comes from the resource struct passed in.
// It converts the data we get back from one request into an array of books.
guard let manyA = resource.parse(data) else { return }
// This is the total running tally of books from all our requests.
allA.appendContentsOf(manyA)
}
}
}
}
class TableViewController: UITableViewController {
var books = [Book]() {
didSet { tableView.reloadData() }
}
override func viewDidLoad() {
super.viewDidLoad()
// Call site
Webservice().loadAll(Book.requests) { [weak self] (books) in
dispatch_async(dispatch_get_main_queue()) {
self?.books.appendContentsOf(books)
}
}
}
//... all your normal methods for cells and stuff
}
class Book {
var id: Int
var title: String
let description: String
var coverImage: String
var isbn: String
var publisherID: Int
var publisherName: String
var authorID: Int
var authorFirstName: String
var authorLastName: String
init(id: Int, title: String, description: String, coverImage: String, isbn: String, publisherID: Int, publisherName: String, authorID: Int, authorFirstName: String, authorLastName: String) {
self.id = id
self.title = title
self.description = description
self.coverImage = coverImage
self.isbn = isbn
self.publisherID = publisherID
self.publisherName = publisherName
self.authorID = authorID
self.authorFirstName = authorFirstName
self.authorLastName = authorLastName
}
typealias JSONDictionary = [String: AnyObject] // syntactic sugar, makes it clearer
convenience init?(fromJson json: JSONDictionary) {
let bookID = json["id"] as! Int
let bookTitle = json["title"] as! String
let bookDescription = json["description"] as! String
let bookCoverImage = json["cover_url"] as! String
let bookISBN = json["isbn"] as! String
// I would use guard let else statements here to avoid the pyramid of doom but it's stylistic
if let bookPublisher = json["publisher"] as? [String: AnyObject] {
let bookPublisherID = bookPublisher["id"] as! Int
let bookPublisherName = bookPublisher["name"] as! String
if let bookAuthor = json["author"] as? [String: AnyObject] {
let bookAuthorID = bookAuthor["id"] as! Int
let bookAuthorFirstname = bookAuthor["first_name"] as! String
let bookAuthorLastName = bookAuthor["last_name"] as! String
self.init(id: bookID, title: bookTitle, description: bookDescription, coverImage: bookCoverImage, isbn: bookISBN, publisherID: bookPublisherID, publisherName: bookPublisherName, authorID: bookAuthorID, authorFirstName: bookAuthorFirstname, authorLastName: bookAuthorLastName)
return
}
}
return nil
}
}
extension Book {
class func getDataFromJson(completionHandler: ([Book]) -> ()) {
var books = [Book]()
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSURLRequest(URL: NSURL(string: "http://example.website.nl/books/highlighted")!)
let task: NSURLSessionDataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
defer { // no matter how you exit the scope this will be called
dispatch_async(dispatch_get_main_queue()) {
completionHandler(books)
}
}
guard let data = data else { return } // still will call the deferred completion handler
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) else {
print("Error deserializing json.")
return // still will call the deferred completion handler
}
if let jsonBooks = json["books"] as? [[String: AnyObject]] {
for jsonBook in jsonBooks {
guard let book = Book(fromJson: jsonBook) else { continue } // skips nil books from failable initializer, depends on how you want to handle that
books.append(book)
}
print(books.count)
}
}
task.resume()
// call the deferred completion handler after leaving scope
}
}