J'essaie de trouver la solution pour un traitement simple de toutes les étapes nécessaires pour la consommation en lecture seule des données JSON distantes sur les appareils iOS. Cela signifie récupérer des données JSON distantes, stocker dans le cache local sur un appareil iOS pour une utilisation hors ligne, actualiser le cache, analyser les données JSON. Je pense que c'est une exigence très courante pour toutes les applications mobiles de nos jours.
Je sais qu'il est possible de télécharger manuellement un fichier JSON distant, de le stocker sur une base de données locale ou un fichier sur un appareil iOS et lorsque le réseau n'est pas disponible, récupérez-le dans le stockage local, sinon téléchargez-le sur Internet. Je le fais manuellement maintenant. :) Mais il y a beaucoup d'étapes qu'il est possible de faire en utilisant des frameworks/bibliothèques, n'est-ce pas?
J'ai donc essayé le framework HanekeSwift qui fait presque tout ce dont j'ai besoin mais il ne fait que mettre en cache le JSON distant (et les images) mais ne rafraîchit pas le cache !! ce qui ne m'est pas utile. Je sais aussi qu'il existe Alamofire et SwiftyJSON mais je n'ai aucune expérience avec eux.
Avez-vous une expérience comment faire cela?
Résumé
Grande question!
Vous pouvez absolument accomplir cela avec une combinaison d'Alamofire et de SwiftyJSON. Ce que je recommanderais, c'est une combinaison de plusieurs choses pour rendre cela aussi simple que possible.
Je pense que vous avez deux approches pour récupérer le JSON.
Option 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
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
}
L'option 1 exploite certainement la plus grande quantité de mise en cache prise en charge Apple. Je pense qu'avec ce que vous essayez de faire, vous devriez pouvoir utiliser le NSURLSessionConfiguration
et un cache policy particulier pour accomplir ce que vous cherchez à faire.
L'option 2 nécessitera un travail beaucoup plus important et sera un peu un système étrange si vous exploitez une politique de cache qui met en cache les données sur le disque . Les téléchargements finiraient par copier des données déjà mises en cache. Voici à quoi ressemblerait le flux si votre demande existait dans votre cache d'URL personnalisé.
URL
fourni via NSOutputStreamC'est assez inutile à moins que vous ne téléchargiez de très gros fichiers. Vous pouvez potentiellement rencontrer des problèmes de mémoire si vous chargez toutes les données de demande en mémoire.
La copie des données mises en cache dans le
URL
fourni sera très probablement implémentée via NSInputStream et NSOutputStream. Tout cela est géré en interne par Apple par le framework Foundation. Cela devrait être un moyen très efficace en termes de mémoire pour déplacer les données. L'inconvénient est que vous devez copier l'ensemble de données avant de pouvoir y accéder. .
NSURLCache
Une autre chose qui peut être très utile ici est la possibilité de récupérer une réponse en cache directement à partir de votre NSURLCache
. Jetez un œil au cachedReponseForRequest:
méthode qui peut être trouvée ici .
SwiftyJSON
La dernière étape consiste à analyser les données JSON en objets de modèle. SwiftyJSON rend cela très facile. Si vous utilisez l'option 1 ci-dessus, vous pouvez utiliser le sérialiseur de réponse personnalisé dans Alamofire-SwiftyJSON . Cela ressemblerait à quelque chose comme ceci:
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.responseSwiftyJSON { (request, response, json, error) in
println(json)
println(error)
}
Maintenant, si vous avez utilisé l'option 2 , vous devrez charger les données à partir du disque, puis initialiser un objet SwiftyJSON et commencer à analyser ce qui ressemblerait à quelque chose comme ceci :
let data = NSData(contentsOfFile: URL.path)!
let json = JSON(data: data)
Cela devrait couvrir tous les outils en ce que vous devriez avoir besoin d'accomplir ce que vous essayez. La façon dont vous concevez la solution exacte dépend certainement de vous, car il existe de nombreuses façons de le faire.
Vous trouverez ci-dessous le code que j'ai utilisé pour mettre en cache mes demandes à l'aide d'Alamofire et de SwiftyJSON - j'espère que cela aide quelqu'un
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()
}
}
}
Il s'agit d'une version Swift 3 basée sur la réponse de Charl (en utilisant SwiftyJSON et 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
}
}