WK WebView calling completion handler but now showing anything - ios

I want to load privacy policy (simple string) from server say, https://*****.com/privacy-policy.html to my app.
What I have tried till now is given below:
class TermsAndConditionsDetailViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var contentView: UIView!
var webView: WKWebView!
func webView(_ webView: WKWebView,
didFinish navigation: WKNavigation!) {
print("loaded")
}
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView()
contentView.addSubview(webView)
let myURL = URL(string: "https://***.com/privacy-policy.html")
let myRequest = URLRequest(url: myURL!)
webView.navigationDelegate = self
//I have tried these both one by one
webView.loadHTMLString("", baseURL: myURL)
webView.load(myRequest)
}
}
When i run this code "loaded" gets printed after 2 - 3 seconds but it doesn't show anything on the screen. I have been searching this issue for some time now and have tried to play around with the code. I have cross checked the constraints of all view specially contentView, but no luck.

You forget to add frame for your WKWebView
just add
webView = WKWebView(frame: self.contentView.frame)
after
webView = WKWebView()
and it will work fine.

Related

WebView is not loading for specific URL in Xcode 9 simulator

I am trying to load a URL using webView in swift 4. I tried https://www.google.co.in and it works fine. and the mobile site for the specific URL works fine with android.
But when I tried this in the simulator, it is loading forever.
my code is below:
import UIKit
class WebViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.whiteLarge)
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
if let url = URL(string: "https://www.myurl.com") {
let request = URLRequest(url: url)
webView.loadRequest(request)
}
}
public func webViewDidStartLoad(_ webView: UIWebView)
{
activityIndicator.center = CGPoint(x: self.view.bounds.size.width/2, y: self.view.bounds.size.height/2)
self.view.addSubview(activityIndicator)
activityIndicator.color = UIColor.red
self.activityIndicator.startAnimating()
}
public func webViewDidFinishLoad(_ webView: UIWebView)
{
self.activityIndicator.stopAnimating()
}
}
I don't think that my code is wrong. but I was wondering if I can do anything to make it work on simulator.
Thanks
I just made a small project with your code and no problem in the simulator, https://www.google.co.in loaded just fine.
I noticed you mention you are using Swift 4, I think you should consider using WebKit View (WKWebView) since UIWebView is deprecated. In Apple's documentation you can see how to implement it, it's pretty straight forward.
If your certificate is not trusted you must add to Info.plist App Transport Security Settings, Allow Arbitrary Loads to YES. (This is not recommended).
The code is really simple, give it a try:
import UIKit
import WebKit
class WebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myURL = URL(string: "https://www.myurl.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
}
}
}

Simple Webview: navigationDelegate crashes on start

I have a small problem with my WebView. This is what I do in my app:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
#IBOutlet var webView: WKWebView! // draged from storyboard
override func viewDidLoad() {
super.viewDidLoad()
var urlToLoad : (String)
urlToLoad = "http://wwww.myapp.com"
webView.sizeToFit()
webView.isUserInteractionEnabled = true
let url = URL(string: urlToLoad)
let request = URLRequest(url:url!)
//webView.navigationDelegate = self
webView.load(request)
}
As you see the row "webView.navigationDelegate = self" is commented. If I uncomment it my app will crash on start and telling me this:
Debugger: libc++abi.dylib: terminating with uncaught exception of type NSException
Any idea of why is this happening and how to solve this? I need this in order to use methods like "didFinish navigation" that I already implemented in the page but they are never called.
Also, by having a look at my code... you see anything I should change regarding the use of my webview?
Thank you for helping me.
You are using UIWebView and trying to call the methods of WKWebView which is leading to crash your app due to unrecognised selector send on UIWebView.
Try to create new project and use below mentioned code.
import "WebKit" framework to your class.
override func viewDidLoad() {
super.viewDidLoad()
let myWebView:WKWebView = WKWebView(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
myWebView.navigationDelegate = self;
let url = URL(string: "https://www.Apple.com")!
myWebView.load(URLRequest(url: url))
self.view.addSubview(myWebView)
}
then implement WKNavigationDelegate method
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
let url = webView.url
print(url as Any) // this will print url address as option field
if url?.absoluteString.range(of: ".pdf") != nil {
pdfBackButton.isHidden = false
print("PDF contain")
}
else {
pdfBackButton.isHidden = true
print("No PDF Contain")
}
}
Hope this will help you!
You are binding an UIWebView to a property of type WKWebView. As a result it crashes when trying to call navigationDelegate on the UIWebView, as it does not offer this method.
There is no template version of WKWebView available in Storyboard, so you have to create the WKWebView programatically:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: view.frame)
webView.navigationDelegate = self
view.addSubview(webView)
let url = URL(string: "https://www.google.com")!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
You don't initialize the webView. The app crashes because it's nil.
You need to bind it to the storyboard or the xib file (uncomment the #IBOutlet) or initialize it programmatically and then add it to you viewController's view.
I was getting the error Value of type '(WKWebView, WKNavigation?) -> ()' has no member 'navigationDelegate' (which I'm guessing was the same issue as what was causing the crash when adding webView.navigationDelegate = self).
I found that the view controller class needs to implement the WKNavigationDelegate protocol (kinda like inheritance) by adding it to the class definition, which fixed the error for me. Make sure to have that as in the first line of this code:
class ViewController: UIViewController, WKNavigationDelegate {
// ...
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// ...
}
}

Separate cookie storage for two (UIWebView or WKWebView)

I want to login many accounts of same site in different webView. For example i have Tab Bar Controller that contains three view controllers and each view controllers contain webView. And for example i embed stackoverflow url for webView in every class. How user can login to different accounts at the same time using these three webView? I've tried this but i can just login one user at a time.
I have found that i need to create separate cookie for every UIWebView, but mostly answers are in objective-c and not the proper answer i want. For example (First Second Third)
Can any one please tell me how i can do it?
class FirstViewController: UIViewController , UIWebViewDelegate{
#IBOutlet weak var webView: UIWebView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
webView.delegate = self
let requestURL = NSURL(string: "http://stackoverflow.com")
let request = NSURLRequest(URL: requestURL!)
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
webView.loadRequest(request)
}
func webViewDidFinishLoad(webView: UIWebView) {
activityIndicator.stopAnimating()
}
}
class SecondViewController: UIViewController, UIWebViewDelegate{
#IBOutlet weak var webView: UIWebView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
webView.delegate = self
let requestURL = NSURL(string: "http://stackoverflow.com")
let request = NSURLRequest(URL: requestURL!)
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
webView.loadRequest(request)
}
func webViewDidFinishLoad(webView: UIWebView) {
activityIndicator.stopAnimating()
}
}
Thanks
You can do this with WKWebView by using different instances of WKWebSiteDataStore:
let configuration1 = WKWebViewConfiguration()
configuration1.websiteDataStore = WKWebsiteDataStore.nonPersistent()
self.webView1 = WKWebView(frame: CGRect.zero, configuration: configuration1)
self.view.addSubview(self.webView1)
let configuration2 = WKWebViewConfiguration()
configuration2.websiteDataStore = WKWebsiteDataStore.nonPersistent()
self.webView2 = WKWebView(frame: CGRect.zero, configuration: configuration2)
Unfortunately, you will loose webView data (such as cookies, cache, etc) after app restart, because non-persistent WKWebsiteDataStore can't be saved to disk (you could notice that WKWebsiteDataStore implements NSCoding, but it doesn't work for non-persistent stores).

How to check if WkWebView finish loading in Objective-C?

I want to load HTML pages using WkWebView and I want to show the page just after it's finished loading. As long as it's loading I would like to show an activity indicator on an empty View.
I create two view a loadingView and a wkWebView. While the page is loading I add to VC as subview the loadingView and after I want to remove loadingView and add wkWebView. Here is my code:
[self addSubview:_loadingView];
_wkWebView = [[WKWebView alloc] initWithFrame:self.frame];
_wkWebView.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
//Send a request to wkUrlconnection
NSURL *wkUrl = [NSURL URLWithString:self.wkUrlString];
NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl];
//Here I want to check if it's loaded and then remove loadingView and add wkWebView
[_wkWebView loadRequest:wkRequest];
[self.loadingView removeFromSuperview];
[self addSubview:_wkWebView];
Can someone show me how to check while it's loading and if finish refresh the VC? Thank you for your answers.
I think the WKNavigationDelegate's webView:didFinishNavigation: delegate callback is what you're looking for.
Configure and present your activity indicator when you start to load and then stop and remove it from view when the callback is called.
For anyone who is experiencing the issue of a webpage containing multiple frames and therefore doing multiple loads which interrups your load animation, I have implemented the following and it works for me in all the situations I have come across so far:
Swift:
var loadCount: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
startLoading()
webview.navigationDelegate = self
let request = URLRequest(url: url)
webview.load(request)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
loadCount += 1
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
loadCount -= 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
if self?.loadCount == 0 {
self?.stopLoading()
}
}
}
The basic idea is to start your load animation before you request the url, then count each request being made and only stop the load animation when your request count == 0. This is done after a slight delay as I find that some frames queue up requests synchronously so the next load will begin before the 0.1 second delay has completed.
( ͡° ͜ʖ ͡°)
for swift 4.2:
func webView(_ webView: WKWebView,
didFinish navigation: WKNavigation!){
print("loaded")
}
be sure to set delegate for webView in didLoad (or similar)
webView.navigationDelegate = self
class WebViewVC: UIViewController {
// MARK: IBOutlets
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
// MARK: Life cycle
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
loadWebsite()
}
}
// MARK: WKWebView
extension WebViewVC: WKNavigationDelegate {
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
activityIndicator.startAnimating()
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityIndicator.stopAnimating()
}
}
// MARK: Private methods
extension WebViewVC {
private func loadWebsite() {
guard let url = URL(string: "google.com") else { return }
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
}
}

Loading webpage inside iOS application

If we load a webpage, we can forward it to safari, but this causes users to leave our app. Is there any way so that a user visits any webpage and then come back to our application.
If you want some browser type functionality for devices earlier then iOS7, you can use this inline browser
iOS 9 Update:
Apple has introduced a visible standard interface for browsing the web i.e. SFSafariViewController for iOS 9+ devices.
Example:
func showTutorial() {
if let url = URL(string: "https://www.example.com") {
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = true
let vc = SFSafariViewController(url: url, configuration: config)
present(vc, animated: true)
}
}
You can use a UIWebView to display the page in your application.
I wanted to add a more detailed answer for others in the future (key coding!) :
In the object library, search for WebKit View and add it to your ViewController
Place "import WebKit" under "import UIKit"
Create an outlet from the WebKit View you placed in your ViewController in your code directly under the opening "{"
Under "superViewDidLoad()", all you need is this:
let url = URL(string: "your_url_here"")
and
webview.load(URLRequest(url:url!))
and dassit!
I'll attach a copy of my code just in case I didn't explain it very well:
import UIKit
import WebKit
class WebsiteViewController: UIViewController {
#IBOutlet weak var webview: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.eventbrite.com/e/pretty-in-petals-tea-party-tickets-43361370025")
webview.load(URLRequest(url: url!))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Use a UIWebView Here's how to program one. Let me know if you need more help or examples
#user1706456 UIWebView is deprecated by Apple, You can use WKWebView for it.
Here's code to do that :
step1: Import WebKit
Step2:
//ViewController's LifeCycle.
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
Step3 :
In ViewDidLoad:
let myURL = URL(string: "www.google.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
Step 4: WKWebView Delegate Methods to handle navigation and loading etc.
//MARK: - WKNavigationDelegate
extension GPWebViewController : WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Refreshing the content in case of editing...
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
}
}

Resources