Search code examples
iosswiftcachingalamofire

Alamofire 4 Swift Cache Control - HTTP Statuscode 304 (If Modified Since)


When I receive a HTTP Status Code 304 from my server, the server doesn't send any content data, because nothing changed.

Now I want to use the cache control from Alamofire 4 (for Swift 3). But I can't figure out how does it work. I find some examples for Alamofire 3 here

 Alamofire.request(req)
    .response {(request, res, data, error) in
        let cachedURLResponse = NSCachedURLResponse(response: res!, data: (data as NSData), userInfo: nil, storagePolicy: .Allowed)
        NSURLCache.sharedURLCache().storeCachedResponse(cachedURLResponse, forRequest: request)
    }

So I think the structure will be similar in Alamofire 4. But where is my content saved? I was hoping that I can do something like this

Pseudocode:

if response.statusCode == 304 {
   return cacheControl.response
}

Does anybody have an idea? In doubt I have write it on my own.


Solution

  • I managed to recover the old cache contents in case the Status code is 304 like this:

    let sessionManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.requestCachePolicy = .reloadIgnoringCacheData
        configuration.timeoutIntervalForRequest = 60
        let memoryCapacity = 500 * 1024 * 1024; // 500 MB
        let diskCapacity = 500 * 1024 * 1024; // 500 MB
        let cache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: "shared_cache")
         configuration.urlCache = cache
        return SessionManager(configuration: configuration)
    }()
    

    And

    func getData(url:URLConvertible,completionHadler:@escaping(Data?,ErrorMessage?)->Void){
    
        let headers: HTTPHeaders =
        [   "Authorization":token!,
        "Accept": "application/json",
        "if-None-Match":  self.loadEtagUserDefault(keyValue: "Etag")
      ]
      
      
      self.sessionManager.request(url, method: .get, parameters:nil, headers: headers)
      
      .validate()
      
      .responseJSON { (response) in
        
        switch (response.result) {
        case .success:
          
          // SAVE THE RESPONSE INSIDE THE CACHE
          
          self.saveCache(response)
          
          //---
          
          if let unwrappedResponse = response.response {
            _ = unwrappedResponse.statusCode
          }
          // If all went well, I'll return the date
             // Recovery Etag from the Header
          let etag =  response.response?.allHeaderFields["Etag"] as? String
          //update in memoria Etag
          self.saveEtagUserDefault(etagValue: etag!, key: "Etag")
          
          print("stato codice: \(String(describing: response.response?.statusCode))")
          completionHadler(response.data,nil)
          
          break
        case .failure(let error):
          print(error.localizedDescription)
          let statusCode = response.response?.statusCode
          let url1:URLRequest? = try! response.request?.asURLRequest()
          
          //Nel caso lo status code è nil perciò il sito non e raggiungibile restituisce la vecchia cache
          guard let _ = statusCode else {
            
            let dataOld = self.loadOldDataCache(url: url1!)
            completionHadler(dataOld,nil)
            
            return
          }
        // If the status code is 304 (no change) I return the old cache
          if statusCode == 304 {
            
            print("beccato codice 304 ***")
            let dataOld = self.loadOldDataCache(url: url1!)
            guard let _ = dataOld else {
              completionHadler(nil,ErrorMessage.error(description: "data nil"))
              return
            }
            completionHadler(dataOld,nil)
            return
          }
          
          
        // *** IN CASE OF ERROR 401 refresh the token and recursively call the same method
          print("error - > \n    \(error.localizedDescription) \n")
          
          print("stato codice2: \(String(describing: statusCode))")
          
        
        }
      
      
    }
    
    }
    
    
    //Save the response in the cache
    private func saveCache(_ response: (DataResponse<Any>)) {
      let cachedResponse = CachedURLResponse(response: response.response!, data: response.data!, userInfo: nil, storagePolicy: .allowed)
      let mycache:URLCache = self.sessionManager.session.configuration.urlCache!
      mycache.storeCachedResponse(cachedResponse, for: response.request!)
    }
    
    
    // Given a Request URL returns the old CACHE in case the site is unresponsive or offline
    private func loadOldDataCache(url:URLRequest)->Data?{
      let myCache:URLCache = self.sessionManager.session.configuration.urlCache!
      let cacheResponse = myCache.cachedResponse(for: url)
      return cacheResponse?.data
    }
    
    // Except in memory Etag
    private func saveEtagUserDefault(etagValue:String,key:String)->Void{
      
      UserDefaults.standard.set(etagValue, forKey:key)
      UserDefaults.standard.synchronize()
      
    }
    // Recovery from the memory Etag
    private func loadEtagUserDefault(keyValue:String)->String{
      return UserDefaults.standard.object(forKey: keyValue) as? String ?? "0"
    }
    
    }