I have webView in which I load some url. I need to set custom header for that URLRequest. For the first request it works as expected, header is received on server side and content is displayed accordingly. However if I open another link from displayed page, headers are lost and request is sent without header.
My lucky guess is that, header is added only for the first time and I have to add it every time when request to load url is sent. However I couldn't find method where can I do so.
Currently I'm setting header in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
myWebView.delegate = self
let url = URL(string: "https://mywebsite.com");
var requestobj = URLRequest(url: url!);
requestobj.addValue("my_request_id", forHTTPHeaderField: "X-Requested-With");
myWebView.loadRequest(requestobj);
}
Am I missing something or should I add header in different place for every request?
Yes, you should add custom headers each time when you create request.
Ok, thanks to iphonic, to pointing at shouldStartLoadWith. I could use that to understand is request new or old one and solve my problem by doing so:
func webView(_ webView: UIWebView,
shouldStartLoadWith request: URLRequest,
navigationType: UIWebViewNavigationType) -> Bool{
if(navigationType == UIWebViewNavigationType.linkClicked)
{
var req = request;
req.addValue("my_request_id", forHTTPHeaderField: "X-Requested-With");
self.myWebView.loadRequest(req);
return false;
}
else {
return true;
}
}
So here I check, if navigation type is clickedLink, then I don't load current request, instead I copy it, reapply custom header and load it into myWebView.
If navigationType isn't linkClicked, I proceed request without changes.
Related
I'm trying to inject session cookies I've acquired separately into a WKWebview request, which turns out to be quite a pain...
I managed to inject the session cookies using this solution, as follows:
// Acquiring the cookies
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: s.request!.url!)
//Appending all the cookies into one raw string.
var cookiesRawString = ""
for c in cookies {
cookiesRawString += "\(c.name)=\(c.value); "
}
var req: URLRequest = try! URLRequest(url: URL, method: method)
// Then the injection itself
request.setValue(cookiesRawString, forHTTPHeaderField: "Cookie")
webView.load(req)
Let me quickly explain the server logic with pseudo code:
Server receive call to endpoint /endpoint1 with initial session cookies appended
It then proceed to redirect the client to /endpoint2 with user generated token appended in the url.
requesting the second endpoint with token appended results in the final redirect to /endpoint3 with Set-Cookie header containing one time session cookies
/endpoint3 with the one time session cookies appended results in 200 response, and the user is recognized.
The problem is that for some reason the when I append the cookies to the initial request using the method above, it results with a redirect loop, while on the Android platform it works flawless (I used there a similar injection method).
The only difference that I saw between them was that the android app injected the cookies only on the initial request, and all the subsequent redirect calls were without those session cookies.
While the ios repeated the initial session cookies on all the redirect calls (even ignoring the server set-cookie header and appending the initial session cookies..).
Am I doing something wrong? How can I make the wkwebview use the injected cookies only on the initial request?
Edit 1: Also tried falling back to UIWebview, but it produces the same results, it seems that injecting the cookies as a header is not good, but I tried using HTTPCookieStorage, but it won't save the cookies!
// the count is 7
var cookiesCount = HTTPCookieStorage.shared.cookies(for: s.request!.url!)?.count
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: s.request!.url!)
for c in cookies {
HTTPCookieStorage.shared.setCookie(c)
}
// Count is still 7!
cookiesCount = HTTPCookieStorage.shared.cookies(for: s.request!.url!)?.count
Edit 2:
Well I found out the UIWebview is using a global instance of the cookie storage, the same as alamofire (which I've used to fetch the session cookies), so there was no need to add the cookies manually, the site recognized the user.
But I still prefer to use WKWebview, as the UIWebview memory leak sky rockets (over 100 mb after couple of web pages navigation!).
Is there a way to use the global cookie jar (used by alamofire) in WKWebview??
I think this might be unrelated, but i had a similar issue.
The reason was the modified cookie was messing up all my subsequent request using NSURL.sharedSession. It turns out the cookie set using WKWebView was wiping out the headers from NSURLSession.sharedSession. I think the cookie storage is shared across multiple sessions. So, i ended up using EphemeralSession, instead of sharedSession.
I managed to get it working on WKWebview, using a hackish solution:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction:
WKNavigationAction, decisionHandler:
#escaping (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.url!.absoluteString
if UserAppendix.isLogin && url != previousNavigateUrl {
previousNavigateUrl = url
if url.contains("/endpoint1") {
let headerFields = navigationAction.request.allHTTPHeaderFields
let headerIsPresent = headerFields!.keys.contains("Cookie")
if headerIsPresent {
decisionHandler(WKNavigationActionPolicy.allow)
} else {
var req = URLRequest(url: navigationAction.request.url!)
let cookies = NetworkAppendix.httpSessionCookies
let values = HTTPCookie.requestHeaderFields(with: cookies)
req.allHTTPHeaderFields = values
webView.load(req)
decisionHandler(WKNavigationActionPolicy.cancel)
}
}
else if firstTime {
firstTime = false
let req = URLRequest(url: navigationAction.request.url!)
webView.load(req)
decisionHandler(WKNavigationActionPolicy.cancel)
}
else {
decisionHandler(.allow)
}
}
else {
decisionHandler(.allow)
}
}
I set the cookies on the first request, and on the second request break the flow and create a new request (with the redirect url), to avoid the session cookies I set on the first request, all subsequent requests are treated the same.
I know it's not perfect, but it gets the job done.
Problem :
actually i am getting url link from api response. and by that link i am loading webview. but when webview load its also showing advertisement so is there any possible way to remove that ad from my webview?
here is my code
override func viewDidLoad() {
super.viewDidLoad()
let url : NSURL = NSURL(string: webviewurl)!
let request : NSURLRequest = NSURLRequest(URL: url)
myweb.loadRequest(request)
}
let me know if is there any possible way to remove ad from webview or may be from url
Usually, you can't change the content of webview you get because what you actually get is a HTML file and then rendered as a webpage.
If the ad only exists in mobile phone, there may be a DNS hijacking,
I try to download images from Amazon S3 server via AlamofireImage framework.
The Images on the S3 server, save with 'Content-Type' = 'binary/octet-stream'.
In the beginning I got the Error:
Failed to validate response due to unacceptable content type.
So, I tried to change/update the HTTP Header's request in order to support with binary/octet-stream'
I updated the method:
private func URLRequestWithURL(URL: NSURL) -> NSURLRequest
In the UIImageView+AlamofireImage.swift file to:
private func URLRequestWithURL(URL: NSURL) -> NSURLRequest {
let mutableURLRequest = NSMutableURLRequest(URL: URL)
mutableURLRequest.addValue("binary/octet-stream", forHTTPHeaderField: "Content-Type")
return mutableURLRequest
}
And is still not working, Just after I added the:
let contentTypes: Set<String> = ["Content-Type", "binary/octet-stream"]
Request.addAcceptableImageContentTypes(contentTypes)
The problem was solved, But I really don't like the fact that I changed a private method in the AlamofireImage framework.
I wonder if there is an elegant way to solve this problem, given I can't change the images 'Content-Type' in the S3 server.
Thanks
Doing Request.addAcceptableImageContentTypes(["binary/octet-stream"]) should be all that you need to get it to work.
If you were using af_setImageWithURL, there was a bug that it wasn't using the acceptableImageContentTypes. AlamofireImage 2.2.0 fixes that.
In Swift 3.2 it's slightly different.
let request = URLRequest(url: URL)
DataRequest.addAcceptableImageContentTypes(["binary/octet-stream"])
AlamoDownloader.shared.imageDownloader.download(request){ response in
DataRequest is an Alamofire public class.
I use WKWebview to load a URL.
let webView = WKWebview()
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url!)
webView.loadRequest(request)
How can I detect if the link the webView should load is broken?
You can use canOpenUrl method:
UIApplication.sharedApplication().canOpenURL(url)
It will do the url validation and if the link is ok it returns true.
It's mostly use before you call:
UIApplication.sharedApplication().openURL(url)
to make sure this link can be open in safari but it should help you here too.
Make sure the link starts with http:// or https://.
Edited:
It will just check is the link is a correct url.
If you want to see the page is offline, authorisation issues, etc. you can implement WKNavigationDelegate protocol and check out this method:
- webView:didFailNavigation:withError:
this should give you more info.
It's always good idea to use: str.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAl‌​lowedCharacterSet())!
it make sure that you don't pass a character which are not allowed in URL.
Edited 2:
To detect the status code you can try to implement:
- webView:decidePolicyForNavigationResponse:decisionHandler:
the navigation response is an NSURLResponse instance but
whenever you make an HTTP request, the NSURLResponse object you get back is actually an instance of the NSHTTPURLResponse class so you should cast it to NSHTTPURLResponse. That should give you a statusCode.
In the last line in the method you should call handler, for example decisionHandler(WKNavigationResponsePolicyAllow).
Ref: Answer exists here
if let url = NSURL(string: yourUrlString) {
var canOpen = UIApplication.sharedApplication().canOpenURL(url)
}
If you want to check if url string is correct and valid - just create NSURL object, if path contains error it will cast to nil:
let string = "http://google.com"
let url = NSURL(string: string)
let brokenString = "http:\\brokenurl.12"
let brokenUrl = NSURL(string: brokenString) // nil - it's not valid!
If you have implemented NSURLConnectionDelegate then below solution can be used.
didFailWithError method of NSURLConnectionDelegate can be used for this.
This method get called if an error occurs during the loading of a resource.
Can refer to below link
https://developer.apple.com/documentation/foundation/nsurlconnectiondelegate/1418443-connection
Technologies: iOS8, SWIFT, XCode 6
Using swift, what is the best way to save an external website's html/css/js, modify that saved data with my own css / js, and then load it in the view. This way, the external page loads with my custom styles/js already implemented.
I'm not sure how complicated your use of the UIWebView is but, the quickest implementation I can think of (aside from the evaluateJS route you've already done):
Create a property to decide if a request has been hijacked yet (by you).
var hijacked = false
provide the UIWebViewDelegate protocol method.
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
//if true it must be our version, reset hijack and allow to load
if hijacked {
hijacked = false
return true
}
//original request. Don't let it load, instead trigger manual loader.
else {
hijacked = true
manuallyLoadPage(request)
return false
}
}
Then you just need a method to fetch the page, get the text, manipulate the text, and then load the page with your version.
func manuallyLoadPage(request: NSURLRequest) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
var html = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
html = html.stringByReplacingOccurrencesOfString("</head>", withString: "<script>alert(\"I added this\")</script></head>", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
self.webView.loadHTMLString(html, baseURL: response.URL!)
}
task.resume()
}
This is just a quick and dirty approach, you may want to do a more thorough job of tracking which requests are hijacked requests etc... This one just assumes an even odd kind of approach. You could obviously manipulate the html however you want, I just added the JS alert as a proof of concept.