Webkit callAsyncJavaScript not waitng for async await function call in SwiftUI

31 views Asked by At

I am using WebKit in SwiftUI to update my indexedDB, but for some reason, the callAsyncJavaScript function of the webkit module is not waiting for the await response, can someone help? What changes or methods should I implement for the correct functionality of this task? I am new to SwiftUI and want to access the indexedDB.

Here is the sample code for reference.

swiftcode.swift -

import SwiftUI
import WebKit

struct IndexedDBView: UIViewRepresentable {
    var jsCode: String

    class Coordinator: NSObject, WKNavigationDelegate {
        var webView: WKWebView?
        var jsCode: String

        init(jsCode: String) {
            self.jsCode = jsCode
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            self.webView = webView
            webView.callAsyncJavaScript(jsCode, in: nil, in: .page) { result in
                switch result {
                case let .failure(error):
                    debugPrint("failure \(error)")
                case let .success(result):
                    print(result)
                }
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(jsCode: jsCode)
    }

    func makeUIView(context: Context) -> WKWebView {
        let webViewConfiguration = WKWebViewConfiguration()
        let webView = WKWebView(frame: .zero, configuration: webViewConfiguration)
        webView.navigationDelegate = context.coordinator
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        if let htmlPath = Bundle.main.path(forResource: "abcd", ofType: "html") {
            let htmlURL = URL(fileURLWithPath: htmlPath)
            uiView.loadFileURL(htmlURL, allowingReadAccessTo: htmlURL)
        }
    }
}

abcd.html -

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/crypto-js.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/idb@8/build/umd.js"></script>
    <script src="yourScript.js"></script>
    <script src="sample.js"></script>
</head>
<body>
    <div id="updateMe">Initial content</div>
</body>
</html>

sample.js -

const getKeyForEncryptAndDecrypt = (userId) => {
    if (userId) {
        let first = '';
        let second = '';
        for (let index = 0; index < userId.length; index += 2) {
          first += userId[index];
          second = userId[index + 1] + second;
        }
        const key = first + second;
        return key;
      }
      return '';
};

    const handleSetPassAndTicketWithDB = async () => {
        var decryptkey = getKeyForEncryptAndDecrypt('9953425561');
        var myIndexDB = IndexDB(objectStoreKeys.myPassHistory);
        let pass = await myIndexDB.get('twqwa', decryptkey);
        var divElement = document.getElementById('updateMe');
        if (divElement) {
            divElement.innerHTML = 'IndexedDB updated with key: ' + JSON.stringify(pass);
        }
        return pass;
    };

And Here is how I am calling the IndexedDBView in my swiftui code -

IndexedDBView(jsCode: "handleSetPassAndTicketWithDB();")

On the screen, I am getting pass as "undefined".

The IndexDB function contains an object of async-await functions, and is written in yourscript.js file.

1

There are 1 answers

0
Pratik Prakash Bindage On
  • The handleSetPassAndTicketWithDB function is defined in the sample.js file, which is loaded in the abcd.html file. However, when you call this function from SwiftUI using webView.callAsyncJavaScript(jsCode, in: nil, in: .page), the function is not found because it's not in the global scope.

  • To fix this issue, you need to make the handleSetPassAndTicketWithDB function global. You can do this by attaching the function to the window object in your sample.js file.

    window.handleSetPassAndTicketWithDB = async () => {
      var decryptkey = getKeyForEncryptAndDecrypt('9953425561');
      var myIndexDB = IndexDB(objectStoreKeys.myPassHistory);
      let pass = await myIndexDB.get('twqwa', decryptkey);
      var divElement = document.getElementById('updateMe');
      if (divElement) {
         divElement.innerHTML = 'IndexedDB updated with key: ' + JSON.stringify(pass);
      }
      return pass;
    };
    
  • The IndexDB function is not defined in your sample.js file. You need to define it before using it.

    window.IndexDB = async (objectStoreKey) => {
      const dbName = 'myDatabase';
      const dbVersion = 1;
      const dbRequest = indexedDB.open(dbName, dbVersion);
    
      dbRequest.onupgradeneeded = (event) => {
          const db = event.target.result;
          if (!db.objectStoreNames.contains(objectStoreKey)) {
              db.createObjectStore(objectStoreKey);
          }
      };
    
      const db = await dbRequest;
    
      return {
          get: async (key, decryptkey) => {
              const transaction = db.transaction(objectStoreKey, 'readonly');
              const objectStore = transaction.objectStore(objectStoreKey);
              const request = objectStore.get(key);
              const result = await request;
              if (result) {
                  const decrypted = CryptoJS.AES.decrypt(result, decryptkey);
                  return decrypted.toString(CryptoJS.enc.Utf8);
              }
              return null;
          },
      };
    };