Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS How to add cookie to each UIWebView request?

I need to open URL with Angular in UIWebView and I need to send cookie with each UIWebView request.

What I tried to do:

I tried to check if request contains cookie. If it does UIWebView performs request, if not I create the same request but with cookie and perform it. To substitute requests I used UIWebViewDelegate's method func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool. But it works not as I expected, some requests performs without cookies.

My code:

final class FieldServiceViewController: UIViewController {

    private var webView = UIWebView()
    private var sessionID = String()

    override func viewDidLoad() {
        super.viewDidLoad()

        _ = JSONAPI.getSessionID().subscribe(onNext: { [weak self] sessionID in
            self?.sessionID = sessionID
            self?.configureUI()
            let string = "https://someURL"
            let url = URL(string: string)
            let request = URLRequest(url: url!)
            self?.webView.loadRequest(request)
        })
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        webView.frame = view.bounds
    }

    private func configureUI() {
        webView.delegate = self
        view.addSubview(webView)
    }

    private func cookedRequest(from: URLRequest) -> URLRequest? {

        let cookiesKey = "Cookie"
        let headers = from.allHTTPHeaderFields ?? [:]
        if (headers.contains { $0.0 == cookiesKey }) {
            return nil
        }

        var request = from
        request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
        let cookiesToAdd = "SESSIONID=\(sessionID)"
        request.addValue(cookiesToAdd, forHTTPHeaderField: cookiesKey)
        return request
        }
    }

    extension FieldServiceViewController: UIWebViewDelegate {

    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {

        if let cooked = cookedRequest(from: request) {
            webView.loadRequest(cooked)
            return false
        }

        return true
    }
}

How to add cookie to each UIWebView request?

P.S. I also saved cookie in HTTPCookieStorage, but looks like there is no connection between UIWebView's requests and shared storage at all.

like image 960
BadCodeDeveloper Avatar asked Jan 17 '18 06:01

BadCodeDeveloper


People also ask

Does Webview share cookies with Safari?

WKWebView is an in-app browser that displays web content. It doesn't share cookies or web site data with other WKWebView instances, or with the Safari browser.

Does WebView have cookies?

Cookies storing is an essential part of Android development, used extensively in authentication. Here I'm sharing… The focus here is fully enabling the Cookies to be shared between the Native and the Webview.

What is the difference between UIWebview and WKWebView?

Unlike UIWebView, which does not support server authentication challenges, WKWebView does. In practical terms, this means that when using WKWebView, you can enter site credentials for password-protected websites.

What is WKWebView?

A WKWebView object is a platform-native view that you use to incorporate web content seamlessly into your app's UI. A web view supports a full web-browsing experience, and presents HTML, CSS, and JavaScript content alongside your app's native views.


2 Answers

I would prefer WKWebView instead of UIWebView as UIWebView is deprecated.

Now about adding the session id to each request made by your Angular project.

Instead of adding the session id by creating new request from delegate method, I would recommend adding a request interceptor at Angular level this will give you more control over each request made by your Angular project.

Register the interceptor via an anonymous factory

$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
  return {
   'request': function(config) {
       config.headers['SESSIONID'] = 'Your Session id here';
    },

    'response': function(response) {

    }
  };
});

Now how you can use this.

You can convert the above code to Swift String and when you have your session id and the Angular Project is loaded you can execute this by wkwebview's evaluatejavaScript method.

e.g.

var sessionId = "you id"
        var s="\n" +
        " $httpProvider.interceptors.push(function($q, dependency1, dependency2) {\n" +
        " return {\n" +
        " \'request\': function(config) {\n" +
        " config.headers[\'SESSIONID\'] = \'\(sessionId)\';\n" +
        " },\n" +
        " \n" +
        " \'response\': function(response) {\n" +
        " \n" +
        " }\n" +
        " };\n" +
        " });"

        webView.evaluateJavaScript(s, completionHandler: {(message,error) in
            print(error ?? "No Error")
        })
like image 78
Sahil Avatar answered Oct 18 '22 18:10

Sahil


You can change your code to this:

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    if request.allHTTPHeaderFields?["SESSIONID"] == nil {
       var request: URLRequest = request
       request.allHTTPHeaderFields?["SESSIONID"] = "your_session_id"
       webView.loadRequest(request)
       return false
    }
    return true
}

UPDATE

As I see, best way to solve your problem is to intercept your requests with URLProtocol. Create next class:

class MyURLProtocol: URLProtocol, NSURLConnectionDelegate, NSURLConnectionDataDelegate {

    static let protocolKey = "MyURLProtocolKey"

    private var connection: NSURLConnection?

    override class func canInit(with request: URLRequest) -> Bool {
        if let isCustom = URLProtocol.property(forKey: MyURLProtocol.protocolKey, in: request) as? Bool{
            return false
        }
        return true
    }

    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        //You can add headers here
        var request = request
        print("request: \(request.url?.absoluteString ?? " ")")
        request.allHTTPHeaderFields?["SESSION_ID"] = "your_session_id"
        return request
    }

    override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
        return super.requestIsCacheEquivalent(a, to: b)
    }

    override func startLoading() {
        let newRequest: NSMutableURLRequest = self.request as! NSMutableURLRequest
        URLProtocol.setProperty(true, forKey: MyURLProtocol.protocolKey, in: newRequest)
        self.connection = NSURLConnection(request: newRequest as URLRequest, delegate: self, startImmediately: true)
    }

    override func stopLoading() {
        self.connection?.cancel()
        self.connection = nil
    }

    //MARK: NSURLConnectionDataDelegate methods
    func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
        self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
    }

    func connection(_ connection: NSURLConnection, didReceive data: Data) {
         self.client?.urlProtocol(self, didLoad: data as Data)
    }

    func connectionDidFinishLoading(_ connection: NSURLConnection) {
        self.client?.urlProtocolDidFinishLoading(self)
    }

    func connection(_ connection: NSURLConnection, didFailWithError error: Error) {
        self.client?.urlProtocol(self, didFailWithError: error)
    }
}

Now in your AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    URLProtocol.registerClass(MyURLProtocol.self)
    return true
}
like image 33
Taras Chernyshenko Avatar answered Oct 18 '22 20:10

Taras Chernyshenko