Swift:使用 Alamofire 和 SwiftyJSON 分页 JSON API

Swift: Using Alamofire and SwiftyJSON with a paged JSON API

使用 Alamofire 和 SwiftyJSON 检索一些 JSON 很简单:

鉴于JSON如

{
    "results": [
       {
         "id": "123",
         "name": "Bob"
       },
       {
          "id": "456",
          "name": "Sally"
       }
 }

此函数将起作用:

func loadSomeJSONData() {
        Alamofire.request(.GET, "http://example.com/json/")
            .responseJSON { (_, _, data, _) in
                let json = JSON(data!)
                if let firstName = json["results"][0]["name"].string {
                    println("first name: \(firstName)") // firstName will equal "Bob"
                }
        }
    }

一切都很好。当我需要从分页 API 加载 JSON 时,我的问题就出现了,也就是说,当数据是从对 API 端点的多次调用中收集的时,JSON 看起来更像是:

 {
    "currentPage": "1",
    "totalPages": "6"
    "results": [
       {
         "id": "123",
         "name": "Bob"
       },
       {
          "id": "456",
          "name": "Sally"
       }
     ]
 }

然后下一个块看起来像:

 {
    "currentPage": "2",
    "totalPages": "6"
    "results": [
       {
         "id": "789",
         "name": "Fred"
       },
       {
          "id": "012",
          "name": "Jane"
       }
     ]
 }

在这种情况下,我可以递归调用一个函数来收集所有 "pages" 但我不确定如何将所有 JSON 片段正确地放在一起:

func loadSomeJSONDataFromPagedEndPoint(page : Int = 1) {
        Alamofire.request(.GET, "http://example.com/json/" + page)
            .responseJSON { (_, _, data, _) in
                let json = JSON(data!)
                if let totalPages = json["totalPages"].description.toInt() {
                    if let currentPage = json["currentPage"].description.toInt() {
                        let pageOfJSON = json["results"]

                        // add pageOfJSON to allJSON somehow??

                        if currentPage < totalPages {
                            self.loadSomeJSONDataFromPagedEndPoint(page: currentPage+1)
                        } else {
                            // done loading all JSON pages
                        }
                 }
 }

var allJSON
loadSomeJSONDataFromPagedEndPoint()

我想要的是将每个 JSON 响应的 "results" 部分最终收集到一个对象数组({ "id": "123", "name": "Bob"} 对象)

额外问题:我不确定为什么我需要做 json["totalPages"].description.toInt() 才能获得 totalPages 的值,一定有更好的方法吗?

你在这里有几个问题,让我们一个一个地回答。

I can't tell from your post if you get valid JSON back for each page call or whether you need to put them altogether to complete the JSON. So let's walk through both cases.

选项 1 - 每个页面JSON有效

您已经非常接近了,您只需要稍微调整一下 JSON 解析并存储结果。这是它的样子。

class PagedDownloader {
    var pagedResults = [AnyObject]()

    func loadSomeJSONDataFromPagedEndPoint(page: Int) {
        let request = Alamofire.request(.GET, "http://example.com/json/\(page)")
        request.responseJSON { [weak self] _, _, jsonData, _ in
            if let strongSelf = self {
                let json = JSON(jsonData!)

                let totalPages = json["totalPages"].stringValue.toInt()!
                let currentPage = json["currentPage"].stringValue.toInt()!

                let results = json["results"].arrayObject!
                strongSelf.pagedResults += results

                if currentPage < totalPages {
                    strongSelf.loadSomeJSONDataFromPagedEndPoint(currentPage + 1)
                } else {
                    strongSelf.parsePagedResults()
                }
            }
        }
    }

    func parsePagedResults() {
        let json = JSON(pagedResults)
        println(json)
    }
}

您似乎对 Swifty 了如指掌JSON 所以我会让您处理 parsePagedResults 实现。

选项 2 - 必须组合页面才能创建有效的 JSON

分页JSON

首先,您无法解析部分 JSON,它根本行不通。 NSJSONSerialization 将失败。这意味着您不能将 responseJSON 序列化程序与分页 JSON 一起使用,因为 data 将始终为 nil 而 error 将始终是 json 序列化错误。长话短说,您需要缓存所有数据直到它有效JSON,然后您才能解析。

存储分页 JSON

如果您要存储它,这就是一个没有 Alamofire 的简单示例。

class Pager {

    let page1 = "{\"currentPage\":\"1\",\"totalPages\":\"3\",\"results\":[{\"id\":\"123\",\"name\":\"Bob\"},"
    let page2 = "{\"id\":\"456\",\"name\":\"Sally\"},{\"id\":\"234\",\"name\":\"Christian\"},"
    let page3 = "{\"id\":\"567\",\"name\":\"Jerry\"},{\"id\":\"345\",\"name\":\"John\"}]}"

    let pages: [String]
    let jsonData: NSMutableData

    init() {
        self.pages = [page1, page2, page3]
        self.jsonData = NSMutableData()
    }

    func downloadPages() {
        for (index, page) in enumerate(pages) {
            jsonData.appendData(page.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
        }

        let json = JSON(data: jsonData)
        println(json)

        if let totalPages = json["totalPages"].string?.toInt() {
            println("Total Pages Value: \(totalPages)")
        }
    }
}

Your bonus question is answered at the end of that code chunk. You don't want to use description from SwiftyJSON, but instead the string optional cast and then optional chain into the toInt method.

使用 Alamofire 进行分页和存储

现在您已经有了一个如何将 JSON 页面写入数据块的简单示例,让我们看看如何将相同的方法用于 Alamofire 中的 response 序列化程序。

class Downloader {
    var jsonData = NSMutableData()
    var totalPagesDownloaded = 0
    let totalPagesToDownload = 6

    func loadSomeJSONDataFromPagedEndPoint() {
        for page in 1...self.totalPagesToDownload {
            let request = Alamofire.request(.GET, "http://example.com/json/\(page)")
            request.response { [weak self] _, _, data, _ in
                if let strongSelf = self {
                    strongSelf.jsonData.appendData(data as NSData)
                    ++strongSelf.totalPagesDownloaded

                    if strongSelf.totalPagesDownloaded == strongSelf.totalPagesToDownload {
                        strongSelf.parseJSONData()
                    }
                }
            }
        }
    }

    func parseJSONData() {
        let json = JSON(data: jsonData)
        println(json)
    }
}

使用 SwiftyJSON

解析结果 JSON

parseJSONData 函数中,只需使用 SwiftyJSON 的所有强大功能即可解析出您需要的值。

I'm pretty sure that covers all your possible use cases and questions. Hope that helps!