1

The goal here is to detect whenever the URL of the webView changes. Since the didCommit and didStartProvisionalNavigation functions of the WKWebView class don't always seem to fire when working with certain elements within a WebView, I now have to add an observer to pick up when the WebView's URL value changes.

So far I have created my own Notification extension with name checkURL:

Swift Progress

extension Notification.Name {
    static let checkURL = Notification.Name("checkURL")
}

NotificationCenter.default.post(name: .checkURL, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(getter: webView.url), name: .checkURL, object: webView.url)

Objective-C Solution (KVO)

// Add an observer
[webView_ addObserver:self forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:NULL];

// Method that gets called when the URL value changes
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    // Code
}

How would I go about applying that Objective-C code to fire a function whenever the URL value changes? I am still trying to wrap my head around Notifications and how they work with object values, so any explanation would be very helpful as well!

Thank you for reading!

EDIT: Just to clear things up, I will reword my question:

Since the didCommit and didStartProvisionalNavigation don't always seem to fire (especially when working with JavaScript-based sites, as well as PHP – you'll constantly see URLs being changed with symbols such as # and whatnot); however, as stated before, the built-in WKWebView functions don't seem to catch those. So what I am trying to do here is find a workaround to catch any sort of changes made to the URL, regardless of if it's just a #, etc. And preferably keeping the code entirely Swift – as I am still learning. :)

JDev
  • 5,168
  • 6
  • 40
  • 61
  • This is the third time you have effectively asked the same question. Please stop it. – matt Jan 03 '17 at 00:16

1 Answers1

2

First, forgive the (quite a few) assumptions in this answer, but I can't comment asking for clarification.

Second, I would recommend giving a good read to the NSNotificationCenter reference. Also if I remember correctly, this writeup about notifications was quite helpful for me at first.

Now to the actual code. If I understand your predicament (other than having to use Objective-C and Swift together, which is always... fun), you are trying to fire a notification on an URL change so that your Swift code can do something with the new URL.

As your swift code is listening for the notification, you don't want to post it there. Rather, the notification should be posted in the KVO callback like this:

// I have also changed the 'URL' to 'url' as I suspect that KVO is case-sensitive
// and replaced NULL with nil as it is generally a better practice to use it.
[webView_ addObserver:self forKeyPath:@"url" options:NSKeyValueObservingOptionNew context:nil];

// Method that gets called when the URL value changes
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (sender == webView_ && [keyPath isEqualToString:@"url"]) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"checkURL" object:self];
    }
}

This would send the notification to any observers, like your Swift class. The Swift code would have to be changed to something like:

extension Notification.Name {
    static let checkURL = Notification.Name("checkURL")
}

// Notice that the call to post(name:object:) has been removed.

// The 'object' parameter is only used for filtering out the sender of the notification.
NotificationCenter.default.addObserver(self, selector: #selector(doSomething(notification:)), name: .checkURL, object: nil)

// We can get the notification in the first parameter.
func doSomething(notification: Notification) {
    // Do something, like reading the URL from the web view.
}

Now if you do not have a reference to the webview in your Swift code, you can pass the value in the userInfo parameter when posting the notification, like this:

[[NSNotificationCenter defaultCenter] postNotificationName:@"checkURL" object:self userInfo:@{ @"url" : webView.url }];

Then you can read the userInfo in the notification callback:

func doSomething(notification: Notification) {
    let url = notification.userInfo["url"]
    // Do something with the url.
}
czaurel
  • 21
  • 4
  • Thanks for your help! I am currently attempting to implement this. I also found this website, which may be useful for implementing a workaround using entirely Swift (and zero Objective-C): http://tulusha.com/kvo-in-swift/ – JDev Jan 02 '17 at 23:38
  • Ah, so what is really needed is to have this all in swift and just fire a notification from there for anyone who needs it? That could use some more clarification in the question ,) – czaurel Jan 02 '17 at 23:45
  • Yes, precisely. Since the `didCommit` and `didStartProvisionalNavigation` don't always seem to fire (especially when working with JavaScript-based sites, as well as PHP – you'll constantly see URLs being changed with symbols such as `#` and whatnot); however, as stated before, the built-in WKWebView functions don't seem to catch those. So what I am trying to do here is find a workaround to catch any sort of changes made to the URL, regardless of if it's just a `#`, etc. – JDev Jan 02 '17 at 23:51
  • If you can make the requirements in the question a bit more clear, I'll happily upate the answer to reflect that. Also, the solution you have linked to is a neat abstraction to KVO but sadly it does not really help when you want to observe already existing variables (like the webview's URL). – czaurel Jan 02 '17 at 23:55
  • Just edited. See original post! – JDev Jan 02 '17 at 23:59
  • Okay, with the reference to your previous question by matt, it seems that what you are effectively trying to do may not require notifications at all. Simply using KVO in swift can work if the main requirement is just to observe the changes in the URL. Regarding the syntax, [this not-so-outdated article](http://en.swifter.tips/kvo/) could help. – czaurel Jan 03 '17 at 10:52
  • As for answering, I mostly agree with matt, seeing that this question does not add much value to the previous one besides showing the next iteration in your tought process (and other questions have already dealt with KVO in Swift, like [this one](http://stackoverflow.com/questions/24092285/is-key-value-observation-kvo-available-in-swift). – czaurel Jan 03 '17 at 10:55