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

在iOS Swift中将远程JSON数据同步到本地缓存存储,ios,swift,http,caching,alamofire,Ios,Swift,Http,Caching,Alamofire,我试图找到一种解决方案,可以简单地处理iOS设备上只读消费远程JSON数据的所有必要步骤。这意味着获取远程JSON数据,存储到iOS设备上的本地缓存以供脱机使用,刷新缓存,解析JSON数据。我认为这是当今所有移动应用的普遍要求 我知道可以手动下载远程JSON文件,将其存储到本地数据库或iOS设备上的文件,当网络不可用时,从本地存储获取它,否则从网络加载它。我现在手动操作。:)但是,希望通过使用框架/库可以完成很多步骤,不是吗 所以我尝试了HanekeSwift框架,它几乎可以做我需要的所有事情,

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

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

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

你有经验怎么做吗

总结

  • Swift中支持iOS8的库或框架
  • 下载远程JSON并存储到本地缓存
  • 从其来源刷新本地缓存的可能性
  • 很好的好处是容易进行JSON解析
好问题

使用Alamofire和SwiftyJSON的组合绝对可以实现这一点。我想推荐的是将几种方法结合起来,尽可能地简化这一过程

我认为获取JSON有两种方法

  • 获取内存中的JSON数据并使用缓存策略
  • 将JSON数据下载到磁盘,直接下载到本地缓存
  • 选项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无疑利用了苹果支持的最大数量的缓存。我认为,通过您正在尝试做的事情,您应该能够利用
    NSURLSessionConfiguration
    和一个特定的配置来完成您想要做的事情

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

  • 提出下载请求
  • 请求被缓存,以便将缓存的数据加载到NSInputStream中
  • 数据通过NSOutputStream写入提供的
    URL
  • 在将数据加载回内存的位置调用响应序列化程序
  • 然后使用SwiftyJSON将数据解析为模型对象
  • 这是相当浪费的,除非你下载的是非常大的文件。如果将所有请求数据加载到内存中,可能会遇到内存问题

    将缓存数据复制到提供的
    URL
    很可能通过NSInputStream和NSOutputStream实现。这都是由Apple通过基础框架内部处理的。这应该是一种非常节省内存的数据移动方式。缺点是需要先复制整个数据集,然后才能访问它

    NSURLCache

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

    SwiftyJSON

    最后一步是将JSON数据解析为模型对象。SwiftyJSON使这变得非常简单。如果您正在使用上面的选项1,则可以在中使用自定义响应序列化程序。这将类似于以下内容:

    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)
    

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

    下面是我使用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()
                }
        }
    }
    

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


    谢谢你的回答!对我来说唯一可能的解决方案是在本地磁盘上存储数据的选项2。当1。网络连接不可用,2。什么时候?ad1-从缓存加载数据(如果可用),ad2-刷新缓存中的数据?可能是个愚蠢的问题,但这是对的吗?如果网络连接不可用,那么您将在
    响应中收到一个网络错误。您当然可以添加逻辑,仅当您有internet连接时才尝试下载数据。至于你的其他问题,我已经对上面的答案做了一些大的更新,内容涉及
    NSURLCache
    和不同的
    NSURLRequestCachePolicy
    s。希望这能让你更好地了解利用苹果现有缓存系统的选择。在我的应用程序中,我会使用选项1和
    NSURLCache
    。我想在没有可用连接时使用缓存响应。在缺少连接的情况下,是否存在一种直接使用缓存而不发出请求的方法?谢谢如果我同时使用这两种东西会发生什么?@cnoon你能解释一下现金数据的有效期是多少吗?它会在服务调用期间自动更新?通过调用动态(不同的请求url和参数)请求,我得到了相同的响应,第一次存储在缓存中。有什么想法吗?@lenooh。是否可以在swift中使用Alamofire离线存储数据3@jayaraj&勒诺。。这是我的代码,。如何在swift 3中实现这种离线存储?是否可以修改缓存数据以在以后更新服务器上的数据?
    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
    
        }
    }