在iOS Swift中将远程JSON数据同步到本地缓存存储

时间:2015-02-09 19:48:46

标签: ios swift http caching alamofire

我正在尝试找到解决方案,以简单处理iOS设备上只读消耗远程JSON数据的所有必要步骤。这意味着获取远程JSON数据,存储到iOS设备上的本地缓存以供脱机使用,刷新缓存,解析JSON数据。我认为现在所有移动应用都是非常普遍的要求。

我知道可以手动下载远程JSON文件,将其存储到iOS设备上的本地数据库或文件中,当网络不可用时,从本地存储中获取它,否则从网上下载。我现在手动完成。 :)但是很多步骤希望通过使用框架/库可以做到,不是吗?

所以我尝试了几乎所有我需要的HanekeSwift框架,但它只缓存远程JSON(和图像),但不刷新缓存!这对我没用。我也知道存在Alamofire和SwiftyJSON,但我对它们没有任何经验。

你有任何经验吗?

摘要

  • Swift中支持iOS8的库或框架
  • 下载远程JSON并存储到本地缓存
  • 从其原点刷新本地缓存的可能性
  • 很好的奖励是简单的JSON解析

enter image description here

3 个答案:

答案 0 :(得分:39)

很棒的问题!

您可以通过Alamofire和SwiftyJSON的组合来完成此任务。我建议的是将几件事情结合起来,尽可能简化。

我认为您有两种获取JSON的方法。

  1. 在内存中获取JSON数据并使用缓存策略
  2. 将JSON数据直接下载到磁盘到本地缓存
  3. 选项1

    // Create a shared URL cache
    let memoryCapacity = 500 * 1024 * 1024; // 500 MB
    let diskCapacity = 500 * 1024 * 1024; // 500 MB
    let cache = NSURLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: "shared_cache")
    
    // Create a custom configuration
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders
    configuration.HTTPAdditionalHeaders = defaultHeaders
    configuration.requestCachePolicy = .UseProtocolCachePolicy // this is the default
    configuration.URLCache = cache
    
    // Create your own manager instance that uses your custom configuration
    let manager = Alamofire.Manager(configuration: configuration)
    
    // Make your request with your custom manager that is caching your requests by default
    manager.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"], encoding: .URL)
           .response { (request, response, data, error) in
               println(request)
               println(response)
               println(error)
    
               // Now parse the data using SwiftyJSON
               // This will come from your custom cache if it is not expired,
               // and from the network if it is expired
           }
    

    选项2

    let URL = NSURL(string: "/whereever/your/local/cache/lives")!
    
    let downloadRequest = Alamofire.download(.GET, "http://httpbin.org/get") { (_, _) -> NSURL in
        return URL
    }
    
    downloadRequest.response { request, response, _, error in
        // Read data into memory from local cache file now in URL
    }
    

    选项1 肯定会利用Apple支持的最大缓存量。我认为,通过你想要做的事情,你应该能够利用NSURLSessionConfiguration和特定的cache policy来完成你想要做的事情。

    选项2 将需要更多的工作,如果您利用实际缓存磁盘上数据的缓存策略,将会是一个奇怪的系统。下载最终将复制已缓存的数据。如果您的请求存在于自定义URL缓存中,那么这就是流程。

    1. 提出下载请求
    2. 缓存请求,以便将缓存的数据加载到NSInputStream
    3. 数据通过NSOutputStream
    4. 写入提供的URL
    5. 调用响应序列化程序,将数据重新加载到内存中
    6. 然后使用SwiftyJSON将数据解析为模型对象
    7. 除非您下载非常大的文件,否则这非常浪费。如果将所有请求数据加载到内存中,则可能会遇到内存问题。

        

      将缓存数据复制到提供的URL很可能是通过NSInputStream和NSOutputStream实现的。这一切都由Apple在Foundation框架内部处理。这应该是一种非常有效的内存移动数据的方法。缺点是您需要复制整个数据集才能访问它。

      <强> NSURLCache

      对您而言,另一件可能非常有用的功能是能够直接从您的NSURLCache获取缓存响应。查看可以找到的cachedReponseForRequest:方法here

      <强> SwiftyJSON

      最后一步是将JSON数据解析为模型对象。 SwiftyJSON让这很容易。如果您使用上面的选项1 ,那么您可以在Alamofire-SwiftyJSON中使用自定义响应序列化程序。这看起来像下面这样:

      Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
               .responseSwiftyJSON { (request, response, json, error) in
                   println(json)
                   println(error)
               }
      

      现在,如果您使用选项2 ,则需要从磁盘加载数据,然后初始化SwiftyJSON对象并开始解析,如下所示:

      let data = NSData(contentsOfFile: URL.path)!
      let json = JSON(data: data)
      

      这应该涵盖您应该完成所尝试的所有工具。你如何构建精确的解决方案当然取决于你,因为有很多可能的方法来做到这一点。

答案 1 :(得分:3)

下面是我用Alamofire和SwiftyJSON缓存我的请求的代码 - 我希望它可以帮助那里的人

func getPlaces(){
    //Request with caching policy
    let request = NSMutableURLRequest(URL: NSURL(string: baseUrl + "/places")!, cachePolicy: .ReturnCacheDataElseLoad, timeoutInterval: 20)
    Alamofire.request(request)
        .responseJSON { (response) in
            let cachedURLResponse = NSCachedURLResponse(response: response.response!, data: (response.data! as NSData), userInfo: nil, storagePolicy: .Allowed)
            NSURLCache.sharedURLCache().storeCachedResponse(cachedURLResponse, forRequest: response.request!)

            guard response.result.error == nil else {
                // got an error in getting the data, need to handle it
                print("error calling GET on /places")
                print(response.result.error!)
                return
            }

            let swiftyJsonVar = JSON(data: cachedURLResponse.data)
            if let resData = swiftyJsonVar["places"].arrayObject  {
                // handle the results as JSON, without a bunch of nested if loops
                self.places = resData

                //print(self.places)

            }
            if self.places.count > 0 {
                self.tableView.reloadData()
            }
    }
}

答案 2 :(得分:1)

这是基于Charl答案的 Swift 3 版本(使用SwiftyJSONAlamofire):

func getData(){

    let query_url = "http://YOUR-URL-HERE"   

    // escape your URL
    let urlAddressEscaped = query_url.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)


    //Request with caching policy
    let request = URLRequest(url: URL(string: urlAddressEscaped!)!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 20)

    Alamofire.request(request)
        .responseJSON { (response) in
            let cachedURLResponse = CachedURLResponse(response: response.response!, data: (response.data! as NSData) as Data, userInfo: nil, storagePolicy: .allowed)
            URLCache.shared.storeCachedResponse(cachedURLResponse, for: response.request!)

            guard response.result.error == nil else {

                // got an error in getting the data, need to handle it
                print("error fetching data from url")
                print(response.result.error!)
                return

            }

            let json = JSON(data: cachedURLResponse.data) // SwiftyJSON

            print(json) // Test if it works

            // do whatever you want with your data here

    }
}