I am working on an app which involves pulling course information from student's portal. On the user's side:
Student is brought to a webview showing the login page from university's website. (https://acorn.utoronto.ca/)
Student puts in the credentials (Utorid and password), clicks login, webview is closed, and student is brought back to the app and will see his/her courses nicely imported and arranged.
Meanwhile, the app should:
Go into user's personal portal page upon successful login and take the user back to app while process the following in background
Click on "Enrol & Manage" in the left sidebar (not sure if it is necessary, but when I view the source on my browser upon login, sometimes XHRs folder doesn't show unless click on that link)
Go to XHRs folder and find the json "enrolledCourses" (https://acorn.utoronto.ca/sws/rest/dashboard/courseRegistration/enrolledCourses)
Retrieve and parse the user's course info and make them available for the app
Source view when student just logged in
Source view when student click on "Enrol & Manage" in the left sidebar, as you can see now "XHRs" is displayed
The file "enrolledCourses" in "XHRs" which contains the course information
Here's my code for the ViewController so far:
import UIKit
import HTMLKit
import WebKit
class UtoridViewController: UIViewController, WKNavigationDelegate {
private let webView: WKWebView = {
let webView_result = WKWebView(frame: .zero)
return webView_result
}()
override func viewDidLoad() {
print("Acorn loading")
super.viewDidLoad()
view.addSubview(webView)
webView.frame = view.bounds
webView.navigationDelegate = self
// Covert URL_string into URL
guard let url = URL(string: Constants.ACORN_URL) else {
return
}
webView.load(URLRequest(url: url))
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!){
}
}
So far, I can get the user to the website, but I have no idea how to proceed...
Any help or guidance will be appreciated! Thanks in advance!!!
its impossible, data on another process...
why don't you want to send an http request to the server and get the required data? if I'm not mistaken, you can take cookies from the webView and use them already for the request
there is of course an option through getting html code or executing arbitrary JS code, but this is a very unstable solution
While using web view to convert my webpage to an app using Xcode the login button does not return to the designated page after login it kind of stays stuck in the login page(The behaviours works okay in a browser though)
Similar behaviour has been observed whoever there is a return to a different page
This is for making my website accessible as an iOS app using web view
I tried using this code snippet:
var backNavigation: WKNavigation?
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if backNavigation?.isEqual(navigation) ?? false {
webView.reload()
backNavigation = nil}
The idea is to allow the application get redirected to the correct page as in the website after login or say cancellation. (The functionality works ok on the website)
I think the solution might be to Allow Arbitrary Loads to yes under app transport security settings. kindly check the link below:
Transport security has blocked a cleartext HTTP
try adding this code
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let webview = WKWebView(frame: .zero, configuration: configuration)
I know that I can generally get the current URL of WKWebView by using the URL property. However I have discovered that when there is a redirect, it will not give me the proper URL.
For example, if I go to http://twitter.com and then click on a link to some other company (ex: http://mycompany.com), then I see a t.co/XXX URL which eventually redirects me to mycompany.com.
However, when I look at WKWebView's URL property, I am seeing "t.co" instead of "mycompany.com".
Strangely, I am never seeing didReceiveServerRedirectForProvisionalNavigation:... called, and when I check URL at didStartProvisionalNavigation:... and decidePolicyForNavigationAction:... I just see the "t.co" URL instead of the "mycompany.com" one.
Also, I will need to know the domain in order to make some adjustments to the body, so I am not sure if I can use JS here.
Please let me know if you have any ideas.
UPDATE: I realized this only happens when I use a custom URL scheme set via setURLSchemeHandler:, which I had omitted from the question originally since I didn't think it was related.
After some playing around, I was able to get things working with the following change in willPerformHTTPRedirection:...:
NSMutableURLRequest *newRequest =
[NSMutableURLRequest requestWithURL:request.URL];
dispatch_async(dispatch_get_main_queue(), ^{
[_webView loadRequest:newRequest];
});
completionHandler(nil);
UPDATE: This has a serious drawback because URLs that are not for the main frame will come into willPerformHTTPRedirection:..., and if the request is re-loaded for all of these the page gets messed up.
I need a way to determine if the URL is from the main frame and only do the reload for those.
Alright, so i made it. https://imgur.com/a/eBSaNn7
The solution is to cancel the navigation and load the request with loadRequest: again.
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame?.isMainFrame != true {
webView.load(navigationAction.request)
}
return nil
}
A week ago I had my Instagram login system working really fine. I don't know what happened (recent limitation in iOS or change something with Instagram API...) but I'm not able to login.
In my Instagram application, I have two URIs saved: http://localhost and http://localhost/
I've got my function to make the login. It's something like this:
func instagramLogIn()->Void{
webViewOutlet.loadRequest(URLRequest(url: URL(string: "https://api.instagram.com/oauth/authorize/?client_id=MY_CLIENT_ID&redirect_uri=http://localhost&response_type=token")!))
}
In run this method in viewDidAppear where Instagram login appears:
override func viewDidAppear(_ animated: Bool) {
instagramLogIn()
}
Of course webView outlet's delegate is set in self so I can listen to every change of the webView. So I do this now:
func webViewDidFinishLoad(_ webView: UIWebView){
let result = webView.request!.mainDocumentURL
print(result)
let result2:URL = URL(string: "/", relativeTo: result)!.absoluteURL
if result2.absoluteString == "http://localhost/"{
This is supposed to be run every time a request ends loading so the first method should redirect to http://localhost/#authToken=MY_AUTH_TOKEN. So in webViewDidFinishLoad() I check if the main URL is localhost and if so I keep doing things. I print "result" and "result2" and what I get is the first url to load (https://api.instagram.com/oauth/authorize/?client_id=MY_CLIENT_ID&redirect_uri=http://localhost&response_type=token) and "result2" prints "http://www.instagram.com".
It's not redirecting to http://localhost. A days ago it did.
What happen if I open the first url in Safari (in my Mac, not iOS)? It redirects perfectly and I get my auth_token.
So the question is: why is not redirecting in iOS? In Mac I've already an open session.
I don't know what's wrong with this. A couple or days it worked fine.
Now i'm using UIWebView and with canInitWithRequest: of NSURLProtocol i can intercept all requests and do with it what I want.
In the new WKWebView this method there isn't, and i not found something similar.
Has someone resolved this problem?
I see that after 5 years this question still generates curiosity, so I describe how I solved it and about some main problems I faced up.
As many who answered here, I have implemented WKURLSchemeHandler and used new schemes.
First of all the URL that wkwebview launches must not be HTTP (or HTTPS) but one of yours new schemes.
Example
mynewscheme://your-server-application.com
In you WKWebViewConfiguration conf, I set the handler:
[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:#"mynewscheme"];
[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:#"mynewschemesecure"];
In CustomSchemeHandler I have implemented webView:startURLSchemeTask: and webView:stopURLSchemeTask:.
In my case I check if the request is for a file that I just saved locally, otherwise I change actual protocol ("mynewscheme or "mynewschemesecure") with http (or https) and I make request by myself.
At this point I solved the "interception problem".
In this new way we have the webview "location" (location.href via javascript) with my new scheme and with it new problems started.
First problem is that my applications work mainly with javascript,
and document.cookie has stopped working. I'm using Cordova
framework, so I've develeped a plugin to set and get cookie to
replace document.cookie (I had to do this, because, obviously, I
have also http header set-cookie).
Second problem is that I've got a lot of "cross-origin" problems, then
I changed all my urls in relative url (or with new schemes)
Third problem is that browser automatically handle server port 80
and 443, omitting them, but has now stopped (maybe because of "not
http location"). In my server code I had to handle this.
Writing down these few rows I admit that it seems to was an easy problem to solve, but I ensure that find out a workaround, how to solve it and integrate with the infinite amount of code has been hard. Every step towards the solution corresponded to a new problem.
You can intercept requests on WKWebView since iOS 8.0 by implementing the decidePolicyFor: navigationAction: method for the WKNavigationDelegate
func webView(_ webView: WKWebView, decidePolicyFor
navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Swift.Void) {
//link to intercept www.example.com
// navigation types: linkActivated, formSubmitted,
// backForward, reload, formResubmitted, other
if navigationAction.navigationType == .linkActivated {
if navigationAction.request.url!.absoluteString == "http://www.example.com" {
//do stuff
//this tells the webview to cancel the request
decisionHandler(.cancel)
return
}
}
//this tells the webview to allow the request
decisionHandler(.allow)
}
there are many ways to implement intercepter request.
setup a local proxy, use WKNavigationDelegate
-[ViewController webView:decidePolicyForNavigationAction:decisionHandler:]
load new request and forward to local server, and at the local server side you can do something(eg. cache).
private api. use WKBrowsingContextController and custom URLProtocol
Class cls = NSClassFromString(#"WKBrowsingContextController");
SEL sel = NSSelectorFromString(#"registerSchemeForCustomProtocol:");
if ([(id)cls respondsToSelector:sel]) {
// 把 http 和 https 请求交给 NSURLProtocol 处理
[(id)cls performSelector:sel withObject:#"http"];
[(id)cls performSelector:sel withObject:#"https"];
}
use KVO to get system http/https handler.(ios 11, *)
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
URLSchemeHandler *schemeHandler = [[URLSchemeHandler alloc] init];
[configuration setURLSchemeHandler:schemeHandler forURLScheme:#"test"];
NSMutableDictionary *handlers = [configuration valueForKey:#"_urlSchemeHandlers"];
handlers[#"http"] = schemeHandler;
handlers[#"https"] = schemeHandler;
all the three ways you probably need handle CORS & post bodies to be stripped,you overwrite change browser`s option request(
Preflighted_requests),you can custom http header to replace http body for post bodies to be stripped.
in iOS 11 WKWebView has come up with Custom Scheme Handler called WKURLSchemeHandler, which you can use to intercept the custom events.
for more info check out this project.
https://github.com/BKRApps/KRWebView
I know I am late but I am able to solve this problem. I can intercept each and every request even your http/https call using below trick. I can also trace the call made from html to server calls. I can also use this to render html with offline content.
Download the html of the website that we want to render in offline or online to intercept the request.
Either place the html in document directory of the user or place it inside the archive. But we should know the path of the html file.
Place all your js, cs, woff, font of our website at the same level as our base html. We need to given permission while loading the web view.
Then we have to register our own custom handler scheme with WKWebView. When wkwebview see the pattern "myhandler-webview" then it will give you control and you will get the callback to 'func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask)' delegate implementation. You can play around with url in this delegate like mentioned in point 6.
let configuration = WKWebViewConfiguration();
configuration.setURLSchemeHandler(self, forURLScheme: "myhandler-webview");
webView = WKWebView(frame: view.bounds, configuration: configuration);
Convert file scheme to the custom scheme (myhandler-webview) then load it with WKWebView
let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
var htmlURL = URL(fileURLWithPath: htmlPath!, isDirectory: false)
htmlURL = self.changeURLScheme(newScheme: "myhandler-webview", forURL: htmlURL)
self.webView.load(URLRequest(url: htmlURL))
Implement below methods of WKURLSchemeHandler protocol and handle didReceiveResponse, didReceiveData, didFinish delegate methods of WKURLSchemeTask.
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
print("Function: \(#function), line: \(#line)")
print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")
// You can find the url pattern by using urlSchemeTask.request.url. and create NSData from your local resource and send the data using 3 delegate method like done below.
// You can also call server api from this native code and return the data to the task.
// You can also cache the data coming from server and use it during offline access of this html.
// When you are returning html the the mime type should be 'text/html'. When you are trying to return Json data then we should change the mime type to 'application/json'.
// For returning json data you need to return NSHTTPURLResponse which has base classs of NSURLResponse with status code 200.
// Handle WKURLSchemeTask delegate methods
let url = changeURLScheme(newScheme: "file", forURL: urlSchemeTask.request.url!)
do {
let data = try Data(contentsOf: url)
urlSchemeTask.didReceive(URLResponse(url: urlSchemeTask.request.url!, mimeType: "text/html", expectedContentLength: data.count, textEncodingName: nil))
urlSchemeTask.didReceive(data)
urlSchemeTask.didFinish()
} catch {
print("Unexpected error when get data from URL: \(url)")
}
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
print("Function: \(#function), line: \(#line)")
print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")
}
Let me know if this explanation is not enough.
Objective c example mentioned below
intercepting request with wkwebview
One can use WKURLSchemeHandler to intercept each and every request to be loaded in WKWebView,
Only disadvantage is that you cannot register http or https scheme for interception,
Solution over that is,
Replace your http/https scheme of url with custom scheme url like xyz://
for e.g. https://google.com can be loaded like xyz://google.com
Now you will get a callback in WKURLSchemeHandler there you again replace it back to https and load data programmatically and call urlSchemeTask.didReceive(response)
This way each and every https request will come to your handler.
I am blindly taking guesses since I only have my windows computer with me. By reading the Apple Developer documentation here is information I gathered that might lead to some ideas on how to solve the question.
Based on WKWebView,
Set the delegate property to an object conforming to the WKUIDelegate protocol to track the loading of web content.
Also, I see we can set our navigationDelegate with the,
weak var navigationDelegate: WKNavigationDelegate? { get set }
The methods of the WKNavigationDelegate protocol help you implement custom behaviors that are triggered during a web view's process of accepting, loading, and completing a navigation request.
Then after we create and set our custom WKNavigationDelegate, we would override some methods to intercept something we might be looking for. I found the Responding to Server Actions section of some interest since they receive a WKNavigation as parameter. Moreover, you might want to skim through WKNavigationAction and WKNavigationResponse see if there is perhaps something which might help us achieve our goal.
BTW, I am just giving some ideas on what to try so that we can solve this question, ideas which might be 100% wrong cause I have not tried them myself.