Swift:将Alamofire和SwiftyJSON与分页JSON API一起使用

时间:2015-02-17 16:06:31

标签: json swift alamofire

使用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"
       }
     ]
 }

在这种情况下,我可以递归调用函数来收集所有“页面”,但我不确定如何正确地将所有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响应的“结果”部分最终收集到一个对象数组中({ "id": "123", "name": "Bob"}个对象)

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

1 个答案:

答案 0 :(得分:15)

这里有几个问题,所以让我们一次拿一个。

  

如果您为每个页面调用获得有效的JSON,或者您是否需要完全放置它们来完成JSON,我无法从您的帖子中看出来。让我们来看看这两种情况。

选项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)
    }
}

您似乎了解SwiftyJSON的方式,因此我将让您处理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)")
        }
    }
}
  

您的奖金问题会在该代码块的末尾回答。您不希望使用SwiftyJSON中的description,而是使用string可选的强制转换,然后使用toInt方法中的可选链。

使用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的所有强大功能来解析所需的值。

  

我很确定这涵盖了所有可能的用例和问题。希望有所帮助!