3

Using Xcode 7.3 iOS 9.2 all in Swift none of that horrible Objective C I have been working on this for a while and been around the block three times now. I have tried this post here and it didn't work http://www.mzan.com/article/32199494-alamofire-how-remove-cache.shtml

I have also try to use apple documentation but that is so crappy I cannot make sense of if.

So what I am doing is making two Alamofire calls to my server. One to test the credentials of the login information to see if the input is right. The second is to download and return the customer information for there viewing. Both are working fine when I call it the first time. The problem is when I logout of the app i clear all the user information from the page. But when I logbook in during the same session, it calls the first one right so if I put in the wrong username or password it returns false even if I login correctly the first time. But when I download the customer data, it keeps downloading the old information from the first time I accessed the user information. It uses the new username and password but still downloads the old stuff.

This is my Login and Logout functions:

//MARK: ButtonControllers
@IBAction func onLoginClick(sender: AnyObject) {

    errorMessageUI.text = "Checking Creditials"
    email = userNameInput.text!
    password = passwordInput.text!
    buildLoginUrl = checkLoginUrl + emailTag + email + passwordTag + password
    print(buildLoginUrl)
    print("Login Cred")
    checkLoginCredentials(buildLoginUrl)
}

@IBAction func onLogoutClick(sender: AnyObject) {

    //null out everything for logout
    email = ""
    password = ""
    self.loginInformation.setObject(self.email, forKey: "email")
    self.loginInformation.setObject(self.password, forKey: "password")
    self.loginInformation.synchronize()
    performSegueWithIdentifier("logoutSegue", sender: self)
    //self.view = LoginView
}

And this is the alamofire calls

//MARK: Check Credentials Method
//Function to log into the server and retrive data
func checkLoginCredentials(myUrl : String)
{
    Alamofire.request(.GET, myUrl)
        .validate(statusCode: 200..<300)
        .responseString { response in
            print("Cred Success: \(response.result.isSuccess)")
            print("Cred Check: \(response.result.value)")
            //clear all url chache
            NSURLCache.sharedURLCache().removeAllCachedResponses()

            if response.result.value != nil
            {
                let checker : String = response.result.value!
                if checker.lowercaseString.rangeOfString("false") != nil {
                    self.canILogin = false
                    self.errorMessageUI.text = "Wrong username or Password try again"
                }else{
                    self.canILogin = true
                    print("Downloading Json file for customer info")
                    self.loadingImage.hidden = false
                    self.downlaodCustomerinfo(self.customerInfoUrl, myUser: self.email, myPass: self.password)
                    //defaults.setBool(textColorSwitch.on, forKey: "DarkText")
                    self.loginInformation.setObject(self.email, forKey: "email")
                    self.loginInformation.setObject(self.password, forKey: "password")
                    self.loginInformation.synchronize()
                }

                print("Login? " + self.canILogin.description ?? "none" )
            }else
            {
                //Stop the program from downloading anything to avoid crashes
                self.loadingImage.hidden = true
                print("I cannot download User Info")
                self.errorMessageUI.text = "A connection error occured"
                //set the json to be empty to avoid a crash
                //reset the json file incase there is anythig in it
                self.downloadJson = ""


            }

    }
}//end of checkLoginCredentials function

//MARK: Download Customer Infoamtion
func downlaodCustomerinfo(myUrl : String, myUser : String, myPass : String)
{

    //clear all url chache
    NSURLCache.sharedURLCache().removeAllCachedResponses()
    print("Username: " + myUser)
    print("Password: " + myPass)
    print("Download Url: " + myUrl )
    print("Jsonfile before download: " + self.downloadJson)
    Alamofire.request(.GET, myUrl)
        .authenticate(user: myUser, password: myPass)
        .validate(statusCode: 200..<300)
        .responseString { response in
            //print("Success: \(response.result.isSuccess)")
            print("Info Download: \(response.result.value)")


            if response.result.value != nil{

                self.downloadJson = response.result.value!
                print("Json file: " + self.downloadJson)
                self.parseCustomerInfo(self.downloadJson)
            }else
            {
                self.loadingImage.hidden = true
                print("I cannot download User Info")
                self.errorMessageUI.text = "A connection error occured"
                //set the json to be empty to avoid a crash
                self.downloadJson = "{}"
            }

    }
}//end of download

UPDATED Code: Cause the system to return false from the Alamofire response

  //Create a non-caching configuration.
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    config.requestCachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
    config.URLCache = nil
    //Create a manager with the non-caching configuration that you created above.
    let manager = Alamofire.Manager(configuration: config)

    print("Username for download: " + myUser)
    print("Password: " + myPass)
    manager.request(.GET, myUrl)
        .authenticate(user: myUser, password: myPass)
        .validate(statusCode: 200..<300)
        .responseString { response in
            //print("Success: \(response.result.isSuccess)")
            print("Info Download: \(response.result.value)")

            if response.result.value != nil{

                self.downloadJson = response.result.value!
                print("Json file: " + self.downloadJson)
                self.parseCustomerInfo(self.downloadJson)
            }else
            {
                self.loadingImage.hidden = true
                print("I cannot download User Info")
                self.errorMessageUI.text = "A connection error occured"
                //set the json to be empty to avoid a crash
                self.downloadJson = "{}"
            }

    }


}//end of downloadCustomer function
MNM
  • 2,673
  • 6
  • 38
  • 73
  • And yes I checked to see what the server was sending in it header file Cache-Control →no-cache, no-store, max-age=0, must-revalidate just like another post suggested was the problem – MNM May 31 '16 at 01:23
  • Your `Manager` on this line `let manager = Alamofire.Manager(configuration: config)` in the updated code, dies. Your manager no longer exists by the time the request starts (if it starts). Your request is failing because when your manager goes out of scope, the request gets cancelled. You should do `print("Error: \(response.result.error)")` and see what it prints. There is a reason I did `self.manager = Alamofire.Manager(configuration: config)`. This will retain the manager and give the request a chance to run. Once the manager deallocates, all pending requests are auto-cancelled. – Brandon May 31 '16 at 04:03
  • I see what you saying but how do I declare a manager : Alamofire.Manager in the main body of the code near where most of the other variables are? – MNM May 31 '16 at 04:51
  • Create a `static instance` of it with a `dispatch_once` block. Added an example to the answer. – Brandon May 31 '16 at 05:26

1 Answers1

7

Why are you not configuring the session? If you configure the session correctly, there will be no caching.

Example:

//Create a non-caching configuration.
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.requestCachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
config.URLCache = nil

//Allow cookies if needed.     
config.HTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()


//Create a manager with the non-caching configuration that you created above.
self.manager = Alamofire.Manager(configuration: config)


//Examples of making a request using the manager you created:

//Regular HTML GET request:
self.manager.request(.GET, "https://stackoverflow.com")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["text/html"])
    .responseString { (response) in
        guard response.result.isSuccess else {
            print("Error: \(response.result.error)")
            return
        }
    print("Result: \(response.result.value)")
}


//JSON GET request:
self.manager.request(.GET, "someURL", parameters: params, encoding: .URL, headers: headers)
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseJSON { (response) in
        guard response.result.isSuccess else {
            print("Error: \(response.result.error)")
            return
        }

        print(response.result.value as? [String: AnyObject])
}

Edit:

let manager = {() -> Alamofire.Manager in
    struct Static {
        static var dispatchOnceToken: dispatch_once_t = 0
        static var instance: Alamofire.Manager!
    }

    dispatch_once(&Static.dispatchOnceToken) {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        config.requestCachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
        config.URLCache = nil

        let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        config.HTTPCookieStorage = cookies
        Static.instance = Alamofire.Manager(configuration: config)
    }

    return Static.instance
}()

manager.request(.GET, "https://stackoverflow.com")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["text/html"])
    .responseString { (response) in
        guard response.result.isSuccess else {
            print("Error: \(response.result.error)")
            return
        }
    print("Result: \(response.result.value)")
}

P.S. You can also look into:

NSURLSessionConfiguration.ephemeralSessionConfiguration() - Returns a session configuration that uses no persistent storage for caches, cookies, or credentials.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • I tried something similar to this before. Can you tell me specifically where each parts go. I know I will replace the Alamofire.request with the self.manager.request. But where does the top part go? Thank you for your response btw – MNM May 31 '16 at 03:18
  • 1
    The top part? The top part is where you initialize your manager. The top part does the configuration and then you create a `Manager` object with that configuration. From then on, all requests that don't need caching should use that manager. – Brandon May 31 '16 at 03:20
  • It just returns nil by using this. For the print(Success ) from the Alamofire response is just printing out false – MNM May 31 '16 at 03:33
  • 1
    Be careful. My example is using `.responseJSON`. Your code in the OP is using `.responseString`. You also have different validation. My code also requires a strict `contentType`. Yours does not. – Brandon May 31 '16 at 03:36
  • I didn't change anything after the .request. All of that is the same. Does that make a difference with the configuration? – MNM May 31 '16 at 03:38
  • 1
    Yes it makes a difference. I added another example of a different kind of request. All that matters is that you use the manager you created. You don't have to use the example requests. – Brandon May 31 '16 at 03:43
  • It still is returning false for success. I posted what I am using in the updated code. Again thank you for your help – MNM May 31 '16 at 03:54
  • 1
    Thank you very much for your help I appreciate it walking me though this issue and teaching me about the dispatch once thing its a useful tool – MNM May 31 '16 at 05:39
  • 2
    `reloadIgnoringLocalAndRemoteCacheData ` "Important This constant is unimplemented and shouldn’t be used." => https://developer.apple.com/reference/foundation/nsurlrequest.cachepolicy/1408722-reloadignoringlocalandremotecach – BoilingLime Oct 10 '16 at 14:09
  • 1
    It's `URLSessionConfiguration.default` and `Alamofire.SessionManager` (plus some other renamings) these days, but the answer otherwise still applies. With the caveat @BoilingLime mentions, of course. – Raphael May 09 '17 at 09:09
  • Why do you have to do both: `config.requestCachePolicy = .ReloadIgnoringLocalAndRemoteCacheData; config.URLCache = nil` if you don't have any cache, then doesn't that mean it has no place to store its cache? or if you're telling it to not cache and ignore cached data, then doesn't that just avoid reading cached results? – mfaani Sep 26 '17 at 02:42
  • @Honey I added cache = nil because the policy right now can not be used until Apple decides to allow it.. – Brandon Sep 27 '17 at 14:07
  • Thanks, I see, in that case why don't you **only** do **either** `reloadIgnoringLocalCacheData` or `cache = nil`? Isn't the end result the same? – mfaani Sep 27 '17 at 14:10
  • 1
    @Honey; Yes, end result is the same.. I just didn't want to change the answer as it already works for OP.. Now you can just do ephemeralSession and skip the whole reloadIgnoringLocalCacheData and cache = nil. Hence the `NSURLSessionConfiguration.ephemeralSessionConfiguration() - Returns a session configuration that uses no persistent storage for caches, cookies, or credentials.` I left all 3 answers in tact. – Brandon Sep 27 '17 at 14:25