I want to open authorised url in WKWebview my application. I have tried all possible solution from stackoverflow and google but none of them working for me.
if let url = myURL {
var request = URLRequest(url: url)
request.addValue("3d66fc991e82415398a8e92d8856da3e", forHTTPHeaderField: "Authorization")
webView.load(request)
}
I have also added this delegate.
public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let creds = URLCredential(user:"user1", password:"1234", persistence: URLCredential.Persistence.permanent)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, creds)
}
Related
I have a WKWebview applying AWS Cognito.
Every request to the server has to be added Authorization into request header.
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/api/v3/graphs?date=2020-08-28") {
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
}
With this code, I already can load the page content but CSS in the page. I checked with chrome (using ModHeader chrome extension to add header) and it works, show correctly, also Android.
I inspected by Chrome and the CSS link in < head > tag like this, it is not the same folder with the HTML file (I don't know if it is the reason).
<link rel="stylesheet" type="text/css" href="https://myserverdomain.amazonaws.com/assets/graphs/style.css"></script>
I can load the css content only with the code:
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/assets/graphs/style.css") {
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
}
UIWebview was deprecated, Is there any way to set WKWebview with a global header as always?
Thank you for your help.
You can redirect all webview's requests to your URLSession with your configuration. To do that you can register your custom URLProtocol for https scheme. There is a hack for WKWebView to intercept url requests with WKBrowsingContextController private class and your URLProtocol implementation e.g.:
class MiddlewareURLProtocol : URLProtocol {
static let handledKey = "handled"
lazy var session : URLSession = {
// Config your headers
let configuration = URLSessionConfiguration.default
//configuration.httpAdditionalHeaders = ["Authorization" : "..."]
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}()
var sessionTask : URLSessionTask?
override var task: URLSessionTask? {
return sessionTask
}
static func registerClass() {
let sel = NSSelectorFromString("registerSchemeForCustomProtocol:")
if let cls = NSClassFromString("WKBrowsingContextController") as? NSObject.Type, cls.responds(to:sel) {
// Register https protocol
cls.perform(sel, with: "https")
}
URLProtocol.registerClass(Self.self)
}
override class func canInit(with request: URLRequest) -> Bool {
return URLProtocol.property(forKey: Self.handledKey, in: request) == nil
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
super.requestIsCacheEquivalent(a, to: b)
}
override func startLoading() {
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
URLProtocol.setProperty(true, forKey: Self.handledKey, in: redirect)
sessionTask = session.dataTask(with: redirect as URLRequest)
task?.resume()
}
override func stopLoading() {
task?.cancel()
}
}
extension MiddlewareURLProtocol : URLSessionDataDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let err = error {
client?.urlProtocol(self, didFailWithError: err)
}
else {
client?.urlProtocolDidFinishLoading(self)
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
client?.urlProtocol(self, didLoad: data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: #escaping (CachedURLResponse?) -> Void) {
completionHandler(proposedResponse)
}
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: #escaping (URLRequest?) -> Void) {
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
Self.removeProperty(forKey: Self.handledKey, in: redirect)
client?.urlProtocol(self, wasRedirectedTo: redirect as URLRequest, redirectResponse: response)
self.task?.cancel()
let error = NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue, userInfo: nil)
client?.urlProtocol(self, didFailWithError: error)
}
}
Just register your protocol on app start to handle all requests:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MiddlewareURLProtocol.registerClass()
...
}
NOTE: To prevent Apple static checks for private classes you can store class names in the array:
let className = ["Controller", "Context", "Browsing", "WK"].reversed().joined()
This code is working fine on Swift version 3, I'm not able to make it work on Swift 4
func rest() {
let path = "https://localhost:8443/someservice"
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let json:JSON = JSON(data: data!)
if let c = json["content"].string {
print(c)
}
})
task.resume()
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
You may need latest syntax
func urlSession(_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
Your Delegate method is right for below swift 4.0 version but it's wrong for swift 4.0 and higher.
Here is working code, You need to use like this.
class ViewController: UIViewController,URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}
}
I am trying to add http header to URLRequest, which I am loading in WKWebView.
I tried this code:
var urlRequest = URLRequest(url: URL(string: "url")!)
urlRequest.addValue("value", forHTTPHeaderField: "key")
self.viewerWebKit.load(urlRequest)
and also this:
var urlRequest = URLRequest(url: URL(string: "url")!)
urlRequest.setValue("value", forHTTPHeaderField: "key")
self.viewerWebKit.load(urlRequest)
But when I am printing http headers with this code:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
let headers = (navigationResponse.response as! HTTPURLResponse).allHeaderFields
for (key,value) in headers {
print("key \(key) value \(value)")
}
decisionHandler(.allow)
}
nothing is added or set. What am I doing wrong?
optional func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
Use this delegate function. You can find headers of request in navigationAction.request.
URLRequest immutable so if you want to add header in that request you need to make it mutable.use this extension to add header.
extension URLRequest {
internal mutating func addHeaders() {
let mutableRequest = ((self as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
mutableRequest.setValue("your header", forHTTPHeaderField: "key")
self = mutableRequest as URLRequest
}
}
}
Use where you want to set Header:
request.addHeaders()
I am using WKWebView to open a url but before that it authenticates the user. It is working fine when we input the correct credentials but in case of wrong credentials, I am unable to find any delegate or function that can detect the failure.
Code:
func webView(_ webView: WKWebView, didReceive challenge:
URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let user = "user"
let password = "password"
let credential = URLCredential(user: user, password: password, persistence: URLCredential.Persistence.forSession)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
}
I can detect the previousFailureCount from URLAuthenticationChallenge, in case of failed response failureResponse always gives status code: 401. Any better way to detect the failure or success for URLAuthenticationChallenge?
You can get status code from the response. Implement the delegate method
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
if let response = navigationResponse.response as? HTTPURLResponse {
if response.statusCode == 401 {
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
Below is the snippet for making request.
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let configurationId = String(format: "NetworkManager%d", UInt16(arc4random_uniform(UInt32(UINT16_MAX))))
let configuration = URLSessionConfiguration.background(withIdentifier: configurationId)
let session = Foundation.URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.current)
let task = session.downloadTask(with: request)
task.resume()
While making this request, the authentication challenge methods as described below are not getting called.
#nonobjc func urlSession(_ session: Foundation.URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void)
#nonobjc func urlSession(_ session: Foundation.URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void)
Try using the following header slightly different from the ones you listed in your question.
open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
//handle challenge here.
}