【问题标题】:Synchronizing remote JSON data to local cache storage in iOS Swift在 iOS Swift 中将远程 JSON 数据同步到本地缓存存储
【发布时间】:2015-04-09 16:19:46
【问题描述】:

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

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

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

你有经验吗?

总结

  • Swift 中支持 iOS8 的库或框架
  • 下载远程 JSON 并存储到本地缓存
  • 可以从其来源刷新本地缓存
  • 不错的好处是简单的 JSON 解析

【问题讨论】:

    标签: ios swift http caching alamofire


    【解决方案1】:

    好问题!

    您完全可以通过结合使用 Alamofire 和 SwiftyJSON 来实现这一点。我建议将几件事结合起来,以使其尽可能简单。

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

    1. 在内存中获取 JSON 数据并使用缓存策略
    2. 将 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 无疑利用了最大数量的 Apple 支持的缓存。我认为,对于您正在尝试做的事情,您应该能够利用NSURLSessionConfiguration 和特定的cache policy 来完成您想要做的事情。

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

    1. 提出下载请求
    2. 请求被缓存,因此缓存的数据被加载到 NSInputStream 中
    3. 数据通过 NSOutputStream 写入提供的URL
    4. 在您将数据加载回内存的位置调用响应序列化程序
    5. 然后使用 SwiftyJSON 将数据解析为模型对象

    除非您下载非常大的文件,否则这非常浪费。如果将所有请求数据加载到内存中,您可能会遇到内存问题。

    将缓存的数据复制到提供的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)
    

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

    【讨论】:

    • 感谢您的回答!对我来说唯一可能的解决方案是将数据存储在本地磁盘上的选项 2。当 1. 网络连接不可用和 2. 何时可用时,Alamofire 究竟做了什么? ad1 - 从缓存中加载数据(如果可用),ad2 - 刷新缓存中的数据?也许是愚蠢的问题,但对吗?
    • 如果网络连接不可用,那么您将在response 中收到网络错误。如果您有互联网连接,您当然可以添加逻辑以仅尝试下载数据。至于您的其他问题,我对上面的答案围绕NSURLCache 和不同的NSURLRequestCachePolicys 做了一些大的更新。希望这可以为您提供更好的方向,帮助您更好地利用 Apple 已经存在的缓存系统。
    • 在我的应用程序中,我会使用选项 1 并使用 NSURLCache。当我没有可用的连接时,我想使用缓存的响应。在没有连接的情况下,是否存在直接使用缓存而不发出请求的方法?谢谢
    • 如果我同时使用这两种东西会怎样?
    • @cnoon 你能解释一下现金数据的到期时间是多少?并且会在服务调用期间自动更新?
    【解决方案2】:

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

    【讨论】:

    • 是否可以修改缓存的数据以便稍后更新服务器上的数据?
    【解决方案3】:

    这是基于 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
    
        }
    }
    

    【讨论】:

    • 通过调用动态(不同的请求url和参数)请求,我得到了相同的响应,它在第一时间存储在缓存中。有什么想法吗?
    • @lenooh。是否可以在 swift 3 中使用 Alamofire 离线存储数据
    • @jayaraj & lenooh .. 这是我的代码,ibb.co/nKKXkk。我怎样才能在 swift 3 中实现这种离线存储
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 2013-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多