What is the best way to implement a callback in iOS with JavaScript?

Attempting to utilize a native function in Swift from wkwebview has been tough. Here's the progress I've made so far:

Swift Part

override func viewDidLoad() {
    super.viewDidLoad()

    // Additional setup after loading the view.
    contentController.add(self, name: "backHomePage")

    config.userContentController = contentController
    self.webView = WKWebView(frame: self.containerView.bounds, configuration: config)

    webView.navigationDelegate = self

    self.containerView.addSubview(self.webView)
    if let url = URL(string: assessmentLink) {
        webView.load(URLRequest(url: url))
    }
}

And

extension WebFormVC: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("javascript sending \(message.name), body: \(message.body)")
    }
}

Javascript

function backHomePage(message) {
    window.webkit.messageHandlers.backHomePage.postMessage(message);
}

The message parameter can be any string, such as "success"

Currently not receiving a call back in the userContentController didReceive method

UPDATE

Tried sending data as key-value pairs, e.g.,

window.webkit.messageHandlers.backHomePage.postMessage({"message" :"Javascript to swift!"});
, but still no success.

Answer №1

Upon investigating your scenario, I have successfully replicated it and all functions are executing as desired.

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {
  let content = """
  <!DOCTYPE html><html><body>
  <button onclick="onClick()">Click me</button>
  <script>
  function onClick() {
    window.webkit.messageHandlers.backHomePage.postMessage("success");
  }
  </script>
  </body></html>
  """

  override func viewDidLoad() {
    super.viewDidLoad()

    let config = WKWebViewConfiguration()
    config.userContentController = WKUserContentController()
    config.userContentController.add(self, name: "backHomePage")

    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 200, height: 200), configuration: config)

    view.addSubview(webView)

    webView.loadHTMLString(content, baseURL: nil)
  }

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print(message.body)
  }
}

Answer №2

In my situation, I encountered an issue where the call to

window.webkit.messageHandlers.backHomePage.postMessage(message);
was located within an external .js file and for some unknown reason it was not being executed.

To resolve this problem, I decided to inject a modified version of the JavaScript function with the same body as in the external .js file at the end of the document.

I want to express my gratitude to @Andy for suggesting the idea of injecting a userScript into the document. While this solution may be useful for others experiencing a similar issue, it may not directly address the question "How to receive callback in iOS from JavaScript?" As such, I have accepted @Andy's answer as it accurately addresses the original question.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to Verify if the Second Tab has Fully Loaded in Selenium Testing

Our current automation process involves using selenium. In order to open 2 URLs simultaneously, we have implemented the following approach: I use driver.get to load the first URL, which includes built-in synchronization for page loading. For the second wi ...

Testing an Angular factory that relies on dependencies using JasmineJS

I have a factory setup like this: angular.module("myServices") .factory("$service1", ["$rootScope", "$service2", function($rootScope, $service2){...})]; Now I'm attempting to test it, but simply injecting $service1 is resulting in an &ap ...

Is it possible to decode JSON using Decodable?

I am facing an issue with a JSON file: { "name": "Jens", "time": "11.45", "date": "2018:04:17", "differentTimestamps":[""] "aWholeLotOfnames":{ "name1": "Karl" "name2": "pär" } What is the proper way to parse the above JSON data? I have referred ...

Adding routes dynamically in nodeJS using Express is a powerful way to manage your server

Is there a way to dynamically add paths to nodeJS within a running program? Instead of relying on kludged solutions like resetting the server when files change, I am looking for a more efficient method. Any suggestions? EDIT: This project is focused on pr ...

Exploring directory organization in GraphQL Queries using GatsbyJS

In my portfolio, I have organized my work into categories, pieces, and pictures in a cascading order similar to a child-parent relationship. The folder structure reflects this hierarchy, with the main problem being explained in more detail below. Folder s ...

In a jQuery conditional statement, selecting the second child of the li element for targeting

I'm encountering an issue where I can't target the second child of an li element and use it in a conditional statement. Are jQuery conditionals not compatible with li:nth-child(2)? if($(".steps ul li:first-child").attr('aria-selected') ...

What is the best way to utilize TypeScript module augmentation with material-ui components?

I have gone through the answers provided in this source and also here in this link, but it appears that they are outdated. I attempted to enhance the type definition for the button component in various ways, including a separate typings file (.d.ts) as we ...

AngularJS - Unexpected crash occurs when the path is modified

Whenever I attempt to change the path of my angular app, about 1 out of every 10 tries results in a routing crash. To clarify, by "crash" I mean that the route changes to the new path, but simultaneously the URL loses the '#' The controller func ...

Activate the front camera on an Android device using HTML5 or JavaScript

I'm currently working on a web app that needs to access the front camera of Android phones running version 4.0 or higher. After taking a picture, the app should then upload the captured image to my own server. Is there a way to launch the front camer ...

Safari is struggling with the backface visibility feature not functioning as expected

I have implemented a cssflip animation in my code where the element rotates on hover. I included transition and backface-visibilty properties. While it works well on Chrome, I faced issues with Safari. I even added the webkit prefix for Safari browser. `. ...

Retrieve the dropdown value that matches the label

How can I ensure that the value of a dropdown option matches its label? Currently, when getting options from a JSON file and populating the dropdown, the values do not match the labels. For example, how can I set the value to be "4.90a-ea02" when the label ...

Column Locking with Merged Rows

I have implemented row spanning in jqgrid by following the instructions provided in this answer: Jqgrid - grouping row level data However, I am facing an issue where setting a column with row span to frozen = true causes the overlay to lose the row spanni ...

Autofill feature for input fields in React Native

Hey there, I've been pondering whether it's possible to achieve a certain functionality using React Native. Specifically, I'm interested in displaying a user's predetermined phone number contact information above the keyboard when focus ...

The loading status will be updated once the data has finished being fetched

I have two action creators in my code - one for fetching data from an API and the other for managing the loading status. However, I am facing an issue where the loading status changes to false before the data is completely fetched. Here are the relevant p ...

What other choices are available for the Angular ui-select2 directive?

Within the Angular select2 controller below: <select ui-select2 id="projectListSelection" data-placeholder="Select a Project ..." ng-model="selectedProject"> @*ng-options="project.WebsiteName for project in projectList"*@ ...

React Navigation version 6 stack navigator is causing the headerLargeTitle to collapse too rapidly

I'm currently implementing React Navigation's stack navigator in my app with the headerLargeTitle and headerTransparent features enabled. This is how my implementation looks: Navigator.tsx <HomeStack.Navigator initialRouteName="Screen ...

Styling of checkboxes in jQuery Mobile is missing after an AJAX request

I am currently working on implementing an ajax call to retrieve a list of items from a json array and display them as checkboxes. While the items are loading correctly, they lack the jquery mobile styling. $(document).ready(function(){ ...

What is the best way to execute a method in a component for each iteration in an *ngFor loop in Angular 2

Is there a way to call a method for each iteration of *ngFor and pass the iterated variable as a parameter? For example: <li *ngFor="let element of componentModel | keys">{{element.key}}--{{element.value}}</li> Then, in the component, I have ...

Tool designed to analyze the timing of sub requests and methods in Node for benchmarking purposes

For my benchmarking and load testing needs, I initially utilized tools such as Apache Bench, Siege, and benchmark.js. However, these tools only provided me with the overall result or time taken from start to finish of the test. I am now seeking a tool or l ...

Contrast between Braces and Quotation Marks in Variable Usage

I've been experimenting with adding an initial empty value to a variable, but I'm confused about the distinctions between the following: var question = ''; var question = {}; Can someone explain the difference between using curly bra ...