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.
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.
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.
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.
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.
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")
})
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With