Swift NSObject class does not reliably respond to delegate method calls - swift

I have had a couple of instances of this recently, so I thought I'd reach out to better understand if I am doing something wrong or iOS/Xcode is just being weird.
I have a utility class, which just inherits from NSObject. My utility class holds reference to a WKWebView, and is the webview's navigationDelegate.
For the life of me, I cannot get the utility class to respond to the navigationDelegate methods, UNLESS I breakpoint at the point of setting the delegate, print out something (anything, from my testing) to the console (yep, this is important. Just breakpointing isn't enough - I have to PRINT something). Then re-run, and it responds as normal.
I am very familiar with iOS development and delegation patterns, but this is behaviour I've seen before and never understood, so I wanted to better my understanding if it's something I am getting wrong.
Here is some example code of what I am doing. It's worth noting, that if I take the utility out of the equation and just use UIViewControllers as the navigationDelegate, everything works fine.
class ExampleUtility: NSObject, WKNavigationDelegate {
private weak var containerViewController: UIViewController?
private let webView: WKWebView
init(containerViewController: UIViewController) {
self.containerViewController = containerViewController
webView = WKWebView(frame: containerViewController.view.frame)
super.init()
setupWebView()
}
private func setupWebView() {
webView.navigationDelegate = self
containerViewController?.view.addSubview(webView)
}
func start() {
guard let url = URL(string: urlString) else {
return
}
let request = URLRequest(url: url)
webview.load(request)
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// This is never called, unless I print ANYTHING to the console
}
}
The fact that it responds after breakpointing and printing makes me thing its a bug of some sort? Also worth nothing that this isn't only on debug runs. It happens when not running from Xcode also.
Any help is greatly appreciated.

Please just keep ExampleUtility object as property in containerViewController.
Then it will work.
The reason you have this issue is, the webview instance is created in the ExampleUtility but the instance of Example Utility was already released since it is not property of containerViewController.
Please try and it will work surely.

Related

WKWebView Navigation Delegate Not Called

I am having difficulty getting a WKWebview to call its delegate methods, namely the did finish one.
I am attempting something similar to this post generating a pdf report using html loaded into a WKWebView;
WKWebView not calling navigation delegate methods
However trying the suggestion there, that doesn't work for me. I have a WKWebView made in a separate class to the View controller that will be calling its methods.
If I strip things right down and just try to simply load a website in the WebView to see if the delegate functions get called I get nothing.
Here is my test class;
import UIKit
import WebKit
class TestWebview: NSObject {
let webView = WKWebView ()
func loadWebsite () {
print ("LOAD TEST CALLED")
webView.navigationDelegate = self
let link = URL(string: "https://www.apple.com")!
let request = URLRequest(url: link)
webView.load(request)
}
}
extension TestWebview: WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
print ("DID COMMIT")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print ("FINISHED LOADING")
}
}
And here is how I am calling it in a separate view controller;
override func viewDidLoad() {
super.viewDidLoad()
let test = TestWebview ()
test.loadWebsite()
}
Although if I put a WKWebView as an outlet into a view controller using Storyboards then the delegate methods do get called. I am trying to avoid this however as I don't want to see a web view in the view controller and want to keep all the code for that separate in another class.
Am I missing something? Checking various other posts on Stack Overflow about this hasn't given me any solutions other than making sure the navigation delegate is being set, which I am. Any help would be greatly appreciated.
You need to move let test = TestWebview () to class level, otherwise this object is going to be evicted when viewDidLoad completes: the navigationDelegate is defined as weak, so it's not going to prevent it either.

WKNavigation delegate functions calling too late

I am loading two websites in two different web views and after loading website i am hiding some content of it which is working perfect using WKNavigationDelegate did finish method.
but issue is did finish function is calling about 90 sec after loading website in web view.
Function is working fine but i just want to know why this function is loading too late it should execute function right after loading website.
my code is
web_view.navigationDelegate = self as? WKNavigationDelegate
web_view.isUserInteractionEnabled = true
let request = URLRequest(url: url!)
self.web_view.load(request)
self.view.addSubview(self.web_view)
delegate method
extension urdu_HomeViewController : WKNavigationDelegate{
//enable javascript to remove vavigation from website
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let removeelementid = "javascript:(function() { " + "document.getElementsByClassName('td-header-menu-wrap-full td-container-wrap')[0].style.display=\"none\"; " + "})()"
webView.evaluateJavaScript(removeelementid) { (res, error) in
if error != nil
{
print("Error")
}
else
{
//print(res!)
}
}
}
WKNavigationDelegate didFinish method is called only after all subresources are loaded. So in your case it might be that some resource takes too much time to load and that delays calling didFinish.
You should rather use WKUserScript to execute your JS right after DOM is ready.

Configuring Interface Builder's new WKWebView object

As some of you may have noticed, Apple recently implemented the WKWebView (from WebKit) as an object in the Interface Builder; however, I am having difficulty with properly implementing it. I am still able to implement it by code, but implementing it via the Interface Builder has proven to be a bit of a pain.
The WKWebView appears blank on launch, and although the NSTextField appears to work when configured, the WKWebView continues to remain blank and doesn't even call upon didStartProvisionalNavigation, didCommit or didFinish when implemented (whereas, when done programatically, this continues to work).
Interface Builder Screenshot
Interface Builder Menu Options
Note: I did attempt to implement it using the WebConfiguration as well, but no luck either. Now I'm just trying to keep the code as simplistic as possible to get a better understanding as to why this is not working.
ViewController.swift
import Cocoa
import WebKit
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// Configure Window Appearance
window!.titlebarAppearsTransparent = true
window!.isMovableByWindowBackground = true
window!.title = ""
window!.backgroundColor = NSColor.white
}
}
class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate {
let myURL = "https://google.com"
#IBOutlet var webView: WKWebView!
#IBOutlet var pageTitle: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Load Homepage URL in WebView
webView.load(URLRequest(url: URL(string: myURL)!))
}
}
So as it turns out, the WKWebView refuses to become active while there is an entitlements file in your project. In my case, I had the App Sandbox enabled with the following settings:
com.apple.security.network.server – YES
com.apple.security.files.user-selected.read-write – YES
Once deleting the entitlements file and removing it entirely from the project, the WKWebView began to work again.

What am I supposed to pass to scrollToEndOfDocument(_:)?

I’m trying to get a WKWebView to scroll down automatically when its content changes. I added this function to my WKNavigationDelegate, which is a subclass of NSViewController:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("Finished loading web view.")
webView.scrollToEndOfDocument(self)
}
It crashes:
-[WKWebView scrollToEndOfDocument:]: unrecognized selector sent to instance 0x6080001a07e0
I guess self is not what I should be passing to scrollToEndOfDocument. I also tried to pass webView or nil instead, with the same result. What value should I pass?
N.B. I tried self because Apple’s docs say:
Declaration
func scrollToEndOfDocument(_ sender: Any?)
Parameters
sender
Typically the object that invoked this method.
You may have to do it via JavaScript:
let script = "window.scrollTo(0, document.body.scrollHeight)"
webView.evaluateJavaScript(script)
This is for old-school WebView; leaving for posterity:
Try calling scrollToEndOfDocument: on the WebView's main frame's document view:
webView.mainFrame.frameView.documentView.scrollToEndOfDocument(nil)
(What you pass in as sender shouldn't matter and has no bearing on the error you're seeing.)

WKNavigationDelegate only works if set to self

I am writing a Swift Hybrid application and I need to be able to know when my WKWebView has finished loading a request. I am attempting to use WKNavigationDelegate to achieve this. However, the only way I can get the events to fire is if I set it this way:
webView.navigationDelegate = self
The problem is I have some data that I want associated with my request, so I created a custom class that implements WKNavigationDelegate that looks like this:
class MyNavigationDelegate : NSObject, WKNavigationDelegate {
init(...) {
//set local variables with passed args
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
println("finished loading")
}
}
then I assign it like this:
webView.navigationDelegate = MyNavigationDelegate(my, arguments, here)
webView.loadRequest(...)
The page is loading so there is no problem with how I'm loading it. What am I missing?
The delegate is a weak property, so make sure that you're holding on to the delegate class instance elsewhere -- it's likely getting deallocated before it even gets used. Meanwhile, self is not getting deallocated, so it is working.