如何在 iOS 中缓存响应并仅在互联网不可用时显示?
How to cache response in iOS and show only when internet is not available?
我想缓存 API 的响应。当互联网可用时,它应该从服务器获取数据,并且每次都应该更新本地缓存的数据,当互联网不可用时,它应该显示缓存的数据。 Alamofire or URLSession
可以吗?还是我需要使用数据库,我应该手动处理这个?
如果你使用 URLRequest
做这样的事情会怎么样
var urlRequest = URLRequest(url: url)
// Configure your URLRequest as you wish with headers etc
// Load from the cache
urlRequest.cachePolicy = .returnCacheDataDontLoad
// Load from the source
if networkStatus == available {
urlRequest.cachePolicy = .reloadIgnoringLocalCacheData
}
let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, response, error in
// You will get data from the source when internet is available
// You will get data from your cache when internet isn't available
if let data = data {
// Reload your UI with new or cached data
}
// There is some error in your request or server
// Or the internet isn't available and the cache is empty
if let error = error {
// Update your UI to show an error accordingly
}
}
task.resume()
根据 OP 评论更新
I'm opening the application first time and the internet is there and
data is loaded from the server. now I'm closing my internet and
opening the app but data will not be displayed because it didn't store
data in the cache when the internet was there because of
reloadIgnoringLocalCacheData this policy. this policy doesn't store
data in cache.
将 cachePolicy
设置为 reloadIgnoringLocalCacheData
并不意味着不在缓存中存储任何数据,这意味着忽略任何存储在缓存中的数据并从源中获取数据。
使用 URLSession 的默认 shared
单例根据 docs
使用默认缓存
来自文档
The shared session uses the shared URLCache, HTTPCookieStorage, and
URLCredentialStorage objects, uses a shared custom networking protocol
list (configured with registerClass(:) and unregisterClass(:)), and
is based on a default configuration.
试一试,我根据你上面提到的用例试过了,它做了你想要它做的事情。
用Alamofire
,可以实现如下,
import Alamofire
class NetworkLogger: EventMonitor {
let queue = DispatchQueue(label: "com.ketan.almofire.networklogger")
func requestDidFinish(_ request: Request) {
print(request.description)
}
func request<Value>(
_ request: DataRequest,
didParseResponse response: DataResponse<Value, AFError>
) {
guard let data = response.data else {
return
}
if let json = try? JSONSerialization
.jsonObject(with: data, options: .mutableContainers) {
print(json)
}
}
}
class APIClient {
private var manager = Session()
init() {
configureSession()
}
private func manageCachePolicy() {
if NetworkReachabilityManager()?.isReachable ?? false {
manager.sessionConfiguration.requestCachePolicy = .reloadIgnoringLocalCacheData
} else {
manager.sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
}
}
private func configureSession() {
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .returnCacheDataElseLoad
if NetworkReachabilityManager()?.isReachable ?? false {
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
}
manager = Session(configuration: configuration, eventMonitors: [NetworkLogger()])
///When we don't want network logs
// manager = Session(configuration: configuration)
}
// MARK: - Request
func requestData(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
completion: @escaping (Result<Data, ErrorResult>) -> Void) {
manageCachePolicy()
manager.request(convertible,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers).validate().responseData
{ (response) in
switch response.result {
case .success(let data):
completion(.success(data))
case .failure(let error):
completion(.failure(.network(string: error.localizedDescription)))
}
}
}
}
我想缓存 API 的响应。当互联网可用时,它应该从服务器获取数据,并且每次都应该更新本地缓存的数据,当互联网不可用时,它应该显示缓存的数据。 Alamofire or URLSession
可以吗?还是我需要使用数据库,我应该手动处理这个?
如果你使用 URLRequest
var urlRequest = URLRequest(url: url)
// Configure your URLRequest as you wish with headers etc
// Load from the cache
urlRequest.cachePolicy = .returnCacheDataDontLoad
// Load from the source
if networkStatus == available {
urlRequest.cachePolicy = .reloadIgnoringLocalCacheData
}
let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, response, error in
// You will get data from the source when internet is available
// You will get data from your cache when internet isn't available
if let data = data {
// Reload your UI with new or cached data
}
// There is some error in your request or server
// Or the internet isn't available and the cache is empty
if let error = error {
// Update your UI to show an error accordingly
}
}
task.resume()
根据 OP 评论更新
I'm opening the application first time and the internet is there and data is loaded from the server. now I'm closing my internet and opening the app but data will not be displayed because it didn't store data in the cache when the internet was there because of reloadIgnoringLocalCacheData this policy. this policy doesn't store data in cache.
将 cachePolicy
设置为 reloadIgnoringLocalCacheData
并不意味着不在缓存中存储任何数据,这意味着忽略任何存储在缓存中的数据并从源中获取数据。
使用 URLSession 的默认 shared
单例根据 docs
来自文档
The shared session uses the shared URLCache, HTTPCookieStorage, and URLCredentialStorage objects, uses a shared custom networking protocol list (configured with registerClass(:) and unregisterClass(:)), and is based on a default configuration.
试一试,我根据你上面提到的用例试过了,它做了你想要它做的事情。
用Alamofire
,可以实现如下,
import Alamofire
class NetworkLogger: EventMonitor {
let queue = DispatchQueue(label: "com.ketan.almofire.networklogger")
func requestDidFinish(_ request: Request) {
print(request.description)
}
func request<Value>(
_ request: DataRequest,
didParseResponse response: DataResponse<Value, AFError>
) {
guard let data = response.data else {
return
}
if let json = try? JSONSerialization
.jsonObject(with: data, options: .mutableContainers) {
print(json)
}
}
}
class APIClient {
private var manager = Session()
init() {
configureSession()
}
private func manageCachePolicy() {
if NetworkReachabilityManager()?.isReachable ?? false {
manager.sessionConfiguration.requestCachePolicy = .reloadIgnoringLocalCacheData
} else {
manager.sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
}
}
private func configureSession() {
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .returnCacheDataElseLoad
if NetworkReachabilityManager()?.isReachable ?? false {
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
}
manager = Session(configuration: configuration, eventMonitors: [NetworkLogger()])
///When we don't want network logs
// manager = Session(configuration: configuration)
}
// MARK: - Request
func requestData(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
completion: @escaping (Result<Data, ErrorResult>) -> Void) {
manageCachePolicy()
manager.request(convertible,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers).validate().responseData
{ (response) in
switch response.result {
case .success(let data):
completion(.success(data))
case .failure(let error):
completion(.failure(.network(string: error.localizedDescription)))
}
}
}
}