Ios 如何在Swift中构造多个HTTP请求?

Ios 如何在Swift中构造多个HTTP请求?,ios,swift,httprequest,Ios,Swift,Httprequest,我对Swift和编程完全是新手。我非常热衷于学习所有正确的方法。因此,任何额外的提示或评论都是非常感谢的 我正在对api执行HTTP请求,这很好。问题是,每个请求只能得到100个结果。我可以设置一个可选的偏移量和限制。如果我将限制设置为101,则会出现一个服务器错误:“请求错误:为限制指定的值无效。允许的最大值为100。”总数为101,因此我需要至少执行两个请求。只有在收到所有请求的总数据后,我才想填充我的tableview。这就是我所拥有的: class Book { var id: Int

我对Swift和编程完全是新手。我非常热衷于学习所有正确的方法。因此,任何额外的提示或评论都是非常感谢的

我正在对api执行HTTP请求,这很好。问题是,每个请求只能得到100个结果。我可以设置一个可选的偏移量和限制。如果我将限制设置为101,则会出现一个服务器错误:“请求错误:为限制指定的值无效。允许的最大值为100。”总数为101,因此我需要至少执行两个请求。只有在收到所有请求的总数据后,我才想填充我的tableview。这就是我所拥有的:

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小时了。我真的在这里和网上搜索了一个例子。我在这里找到的小东西帮不了我

我的想法是如何做到这一点:

  • 发出第一个请求->在某处存储数据
  • 发出第二个请求->向存储的数据添加数据
  • 发出最后一个请求->向存储的数据添加数据
  • 发送数据以填充tableview
  • 我应该使用URL数组并进行迭代吗 通过它,然后将数据附加到某个地方

    我希望有人能帮助我。我会非常感激的

    提前感谢。

    直接回答问题:
    你说“问题是每个请求只能有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
        }
    }