将远程 JSON 数据同步到 iOS Swift 中的本地缓存存储
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28418035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Synchronizing remote JSON data to local cache storage in iOS Swift
提问by kolisko
I am trying to find the solution for simple processing all necessary steps for read-only consuming remote JSON data on iOS devices. It means fetching remote JSON data, store to local cache on iOS device for offline usage, refresh the cache, parsing JSON data. I think it is very common requirement for all mobile apps nowadays.
我试图找到简单处理所有必要步骤的解决方案,以便在 iOS 设备上以只读方式使用远程 JSON 数据。这意味着获取远程 JSON 数据,存储到 iOS 设备上的本地缓存以供离线使用,刷新缓存,解析 JSON 数据。我认为这是当今所有移动应用程序非常普遍的要求。
I know it is possible to manually download remote JSON file, store it to local db or file on iOS device and when network is not available fetch it from local storage otherwise dowload it from net. I do it manually now. :) But it is lot of steps which hope is possible to do by using frameworks/libraries, isn't?
我知道可以手动下载远程 JSON 文件,将其存储到本地数据库或 iOS 设备上的文件,当网络不可用时,从本地存储中获取它,否则从网络下载它。我现在手动完成。:) 但是希望通过使用框架/库可以完成很多步骤,不是吗?
So I tried HanekeSwift framework which do almost everything what I need but it only do caching remote JSON (and Images) but doesn't refresh the cache!! which is not useful for me. I know also that exists Alamofire and SwiftyJSON but I don't have any experience with them.
所以我尝试了 HanekeSwift 框架,它几乎可以做我需要的所有事情,但它只缓存远程 JSON(和图像)但不刷新缓存!!这对我没有用。我也知道存在 Alamofire 和 SwiftyJSON,但我对它们没有任何经验。
Do you have any experience how to do that?
你有没有经验如何做到这一点?
Summary
概括
- libraries or frameworks for iOS8 support in Swift
- download remote JSON and store to local cache
- possibility to refresh local cache from it's origin
- nice bonus is easy JSON parsing
- Swift 中支持 iOS8 的库或框架
- 下载远程 JSON 并存储到本地缓存
- 从它的来源刷新本地缓存的可能性
- 不错的奖励是简单的 JSON 解析
采纳答案by cnoon
Great question!
好问题!
You can absolutely accomplish this with a combination of Alamofire and SwiftyJSON. What I would recommend is a combination of several things to make this as easy as possible.
你绝对可以通过结合 Alamofire 和 SwiftyJSON 来实现这一点。我建议将几件事结合起来,使这尽可能简单。
I think you have two approaches to fetching the JSON.
我认为您有两种获取 JSON 的方法。
- Fetch the JSON data in-memory and use a cache policy
- Download the JSON data to disk directly to your local cache
- 在内存中获取 JSON 数据并使用缓存策略
- 将 JSON 数据直接下载到磁盘到本地缓存
Option 1
选项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
}
Option 2
选项 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
}
Option 1certainly leverages the largest amount of Apple supported caching. I think with what you're trying to do, you should be able to leverage the NSURLSessionConfiguration
and a particular cache policyto accomplish what you're looking to do.
选项 1当然利用了 Apple 支持的最大量的缓存。我认为对于您正在尝试做的事情,您应该能够利用NSURLSessionConfiguration
特定的缓存策略来完成您想要做的事情。
Option 2will require a much larger amount of work, and will be a bit of a strange system if you leverage a cache policy that actually caches data on disk. Downloads would end up copying already cached data. Here's what the flow would be like if your request existed in your custom url cache.
选项 2将需要大量的工作,如果您利用实际将数据缓存在磁盘上的缓存策略,这将是一个有点奇怪的系统。下载最终会复制已经缓存的数据。如果您的请求存在于您的自定义 url 缓存中,则流程如下所示。
- Make download request
- Request is cached so cached data loaded into NSInputStream
- Data is written to the provided
URL
through NSOutputStream - Response serializer is called where you load the data back into memory
- Data is then parsed using SwiftyJSON into model objects
- 发出下载请求
- 请求被缓存,因此缓存数据加载到 NSInputStream
- 数据
URL
通过 NSOutputStream写入提供的 - 响应序列化器在您将数据加载回内存时被调用
- 然后使用 SwiftyJSON 将数据解析为模型对象
This is quite wasteful unless you are downloading very large files. You could potentially run into memory issues if you load all the request data into memory.
除非您下载非常大的文件,否则这是非常浪费的。如果将所有请求数据加载到内存中,则可能会遇到内存问题。
Copying the cached data to the provided
URL
will most likely be implemented through NSInputStream and NSOutputStream. This is all handled internally by Apple by the Foundation framework. This should be a very memory efficient way to move the data. The downside is that you need to copy the entire dataset before you can access it.
将缓存数据复制到所提供的数据
URL
很可能是通过 NSInputStream 和 NSOutputStream 实现的。这一切都由 Apple 通过 Foundation 框架在内部处理。这应该是移动数据的一种非常有效的内存方式。缺点是您需要先复制整个数据集,然后才能访问它。
NSURLCache
网址缓存
One other thing that may be very useful here for you is the ability to fetch a cached response directly from your NSURLCache
. Take a look at the cachedReponseForRequest:
method which can be found here.
在这里对您可能非常有用的另一件事是能够直接从您的NSURLCache
. 看看cachedReponseForRequest:
可以在这里找到的方法。
SwiftyJSON
SwiftyJSON
The last step is parsing the JSON data into model objects. SwiftyJSON makes this very easy. If you're using Option 1from above, then you could use the custom response serializer in the Alamofire-SwiftyJSON. That would look something like the following:
最后一步是将 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)
}
Now if you used Option 2, you'll need to load the data from disk, then initialize a SwiftyJSON object and begin parsing which would look something like this:
现在,如果您使用Option 2,则需要从磁盘加载数据,然后初始化一个 SwiftyJSON 对象并开始解析,它看起来像这样:
let data = NSData(contentsOfFile: URL.path)!
let json = JSON(data: data)
That should cover all the tools in that you should need to accomplish what you attempting. How you architect the exact solution is certainly up to you since there are so many possible ways to do it.
这应该涵盖完成尝试所需的所有工具。您如何构建确切的解决方案当然取决于您,因为有很多可能的方法来做到这一点。
回答by Chazo
Below is the code i used to cache my requests using Alamofire and SwiftyJSON - I hope it helps someone out there
下面是我用来使用 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()
}
}
}
回答by lenooh
This is a Swift 3version based on Charl's answer (using SwiftyJSONand Alamofire):
这是基于 Charl 的答案的Swift 3版本(使用SwiftyJSON和Alamofire):
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
}
}