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

我试图find解决scheme,以简单处理iOS设备上的只读消费远程JSON数据的所有必要步骤。 这意味着获取远程JSON数据,存储到iOS设备上的本地caching以供离线使用,刷新caching,parsingJSON数据。 我认为现在所有的移动应用都是非常普遍的需求。

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

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

你有什么经验如何做到这一点?

概要

  • Swift中支持iOS8的库或框架
  • 下载远程JSON并存储到本地caching
  • 从原点刷新本地caching的可能性
  • 好的奖金是简单的JSONparsing

在这里输入图像说明

谢谢! 米哈尔

好问题!

你可以完全用Alamofire和SwiftyJSON的组合完成这个任务。 我会推荐几个事情的组合,以尽可能简单。

我认为你有两种方法来获取JSON。

  1. 获取内存中的JSON数据并使用caching策略
  2. 将JSON数据直接下载到本地caching

选项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支持的caching。 我想你正在尝试做什么,你应该能够利用NSURLSessionConfiguration和一个特定的caching策略来完成你想要做的事情。

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

  1. 做下载请求
  2. caching请求caching的数据加载到NSInputStream
  3. 数据通过NSOutputStream写入提供的URL
  4. 在您将数据加载回内存的地方调用响应序列化程序
  5. 然后使用SwiftyJSON将数据parsing到模型对象中

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

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

NSURLCache

另一个可能对你非常有用的事情是直接从NSURLCache获取caching响应的能力。 看看可以在这里find的cachedReponseForRequest:方法。

SwiftyJSON

最后一步是将JSON数据parsing为模型对象。 SwiftyJSON使这非常简单。 如果您使用上面的选项1 ,则可以在Alamofire-SwiftyJSON中使用自定义响应序列化程序 。 这看起来像下面这样:

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

现在,如果你使用了Option 2 ,你需要从磁盘加载数据,然后初始化一个SwiftyJSON对象,并开始parsing,看起来像这样:

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

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

下面是我用来caching我的请求使用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() } } } 

这是一个基于查尔的答案(使用SwiftyJSON和Alamofire )的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 } }