Search code examples
swiftcookiesauthenticationnsurlrequestnsurlsession

Swift NSURLSession NSURLRequest Cookies are not accepted


I'm new to swift and OS X programming. For my first steps I wanted to create a command line tool which logs me into the webinterface of my mobile carrier, and then shows the amount of my data left. I began to write a wrapper around NSURLSession to make things easier for me.

Problem: My program won't accept cookies.

I tried a lot of things, also setting cookie policies on the session object but nothing changed. How can I make my program accept cookies and how to use them in subsequent requests?

HttpClient.swift:

import Foundation

class HttpClient {

    private var url: NSURL!
    private var session: NSURLSession

    internal init(url: String) {
        self.url = NSURL(string: url)
        self.session = NSURLSession.sharedSession()
        session.configuration.HTTPShouldSetCookies = true
        session.configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
        session.configuration.HTTPCookieStorage?.cookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
    }

    internal func sendGet() -> String {
        var ready = false
        var content: String!
        var request = NSMutableURLRequest(URL: self.url)

        var task = session.dataTaskWithRequest(request) {
            (data, response, error) -> Void in
            content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
            ready = true
        }
        task.resume()
        while !ready {
            usleep(10)
        }
        if content != nil {
            return content
        } else {
            return ""
        }
    }

    internal func sendPost(params: String) -> String {
        var ready = false
        var content: String!
        var request = NSMutableURLRequest(URL: self.url)

        request.HTTPMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.HTTPBody = params.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
        request.HTTPShouldHandleCookies = true

        var task = session.dataTaskWithRequest(request) {
            (data, response, error) -> Void in
            content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
            ready = true
        }
        task.resume()
        while !ready {
            usleep(10)
        }
        if content != nil {
            return content
        } else {
            return ""
        }
    }

    internal func setUrl(url: String) {
        self.url = NSURL(string: url)
    }
}

main.swift

import Foundation

let loginPage = "https://service.winsim.de/"
let dataPage = "https://service.winsim.de/mytariff/invoice/showGprsDataUsage"

var hc = HttpClient(url: loginPage)
println(hc.sendPost("usernameField=username&passwordfield=password"))
hc.setUrl(dataPage)
println(hc.sendGet())

Solution

  • Issue is solved

    In fact, cookies are accepted with the above code. I tried logging in to a different site and it worked. Also the login persisted. So why it did not work with my carrier's website?

    Stupid me, my carrier has CSRF protection and other hidden form fields which I did not pay attention to. Hence, login did not work. Now I know how to fix it.

    For anyone interested, I'll post my updated HttpClient.swift file which is a bit more tidy, I hope.

    Please feel free to comment on my code and give me hints for improvement.

    import Foundation
    
    public class HttpClient {
    
        private var session: NSURLSession
        private var request: NSMutableURLRequest
    
        public init(url: String) {
            self.session = NSURLSession.sharedSession()
            session.configuration.HTTPShouldSetCookies = true
            session.configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
            session.configuration.HTTPCookieStorage?.cookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
            self.request = NSMutableURLRequest(URL: NSURL(string: url)!)
        }
    
        public func send() -> String {
            var ready = false
            var content: String!
    
            var task = session.dataTaskWithRequest(self.request) {
                (data, response, error) -> Void in
                content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
                ready = true
            }
            task.resume()
            while !ready {
                usleep(10)
            }
            if content != nil {
                return content
            } else {
                return ""
            }
        }
    
        public func setUrl(url: String) -> HttpClient {
            self.request.URL = NSURL(string: url)
            return self
        }
    
        public func getMethod() -> String {
            return self.request.HTTPMethod
        }
    
        public func setMethod(method: String) -> HttpClient {
            self.request.HTTPMethod = method
            return self
        }
    
        public func addFormData(data: Dictionary<String, String>) -> HttpClient {
            var params: String = ""
            var ctHeader: String? = self.request.valueForHTTPHeaderField("Content-Type")
            let ctForm: String = "application/x-www-form-urlencoded"
    
            if(data.count > 0) {
                for(name, value) in data {
                    params += name + "=" + value + "&"
                }
    
                params = params.substringToIndex(params.endIndex.predecessor())
                self.request.HTTPBody = params.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
    
                if ctHeader != nil {
                    self.request.setValue(ctForm, forHTTPHeaderField: "Content-Type")
                }
            }
    
            return self
        }
    
        public func removeFormData() -> HttpClient {
            self.request.setValue("text/html", forHTTPHeaderField: "Content-Type")
            self.request.HTTPBody = "".dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
    
            return self
        }
    }