4

I'm making a hybrid app and using WKWebView. I need to pass a JavaScript Object to the emitter command to open the edit dialog. Here is my code:

        let statDict: [String: Any] = [
            "income" : account.stat.income,
            "expense" : account.stat.expense,
            "summary" : account.stat.summary,
            "incomeShorten" : account.stat.incomeShorten,
            "expenseShorten" : account.stat.expenseShorten,
            "summaryShorten": account.stat.summaryShorten
            ]
        let accountDict: [String: Any] = [
            "id": account.id,
            "name": account.name,
            "description": "",
            "icon": account.icon,
            "currency": account.currency,
            "customer_contact_id": account.customer_contact_id ?? 0,
            "is_archived": account.is_archived,
            "sort": account.sort,
            "create_datetime": account.create_datetime,
            "update_datetime": account.update_datetime ?? "",
            "stat": statDict
        ]

        let accountData = try! JSONSerialization.data(withJSONObject: accountDict, options: JSONSerialization.WritingOptions(rawValue: 0))

        guard let accountString = String(data: accountData, encoding: .utf8) else {
            return
        }
        webView.evaluateJavaScript("function parse(string){ return JSON.parse(string)}") { result, error in
            if error == nil { // this is returns correct staff
                
            }
        }
        webView.evaluateJavaScript("parse('\(accountString)')") { object, error in
            if error == nil {
                let object = object as AnyObject
                print("parse object \(object)")
                
                webView.evaluateJavaScript("window.emitter.emit('openDialog', 'Account', \(object))") { (result, error) in
                    if error == nil { // here the error "Unexpected token '='..."
                        webView.evaluateJavaScript("window.emitter.on('closeDialog', function(){  window.webkit.messageHandlers.emitterMessage.postMessage('closeDialog'); })") { (result, error) in
                            if error == nil {
                                
                            }
                        }
                        webView.evaluateJavaScript("window.emitter.on('createAccount', function(){  window.webkit.messageHandlers.emitterMessage.postMessage('createAccount'); })") { (result, error) in
                            if error == nil {
                                
                            }
                        }
                    } else {
                        print(error as Any)
                    }
                }
            }
        }

The \ (object) returned by the function looks like this:

    {
    "create_datetime" = "2021-08-24 19:19:28";
    currency = RUB;
    "customer_contact_id" = 1;
    description = "";
    icon = "";
    id = 7;
    "is_archived" = 0;
    name = "Business 111";
    sort = 0;
    stat =     {
        expense = 0;
        expenseShorten = 0;
        income = 300000;
        incomeShorten = 300K;
        summary = 300000;
        summaryShorten = 300K;
    };
    "update_datetime" = "";
}

but it should look like this:

{
  create_datetime: "2021-08-24 19:19:28",
  currency: "RUB",
  customer_contact_id: 1,
  description: "",
  icon: "",
  id: 7,
  is_archived: false,
  name: "Business 111",
  sort: 0,
  stat: {
    expense: 0,
    expenseShorten: "0",
    income: 300000,
    incomeShorten: "300K",
    summary: 300000,
    summaryShorten: "300K"
  },
  update_datetime: ""
}

With such an object, the compiler generates the error Unexpected token '='. Expected an identifier as property name.

The parse (string) function will return the correct object if you run it in the js compiler, but in swift the output is not correct.

How to bring an object to the correct form?

andrey leganov
  • 101
  • 1
  • 4

1 Answers1

1

You are trying to pass the string interpolated representation of a Swift object (NSMutableDictionary in your case) to Javascript. Instead you can directly pass the JSON representation to JS context since JSON is a native Javascript object it should do what you are trying to achieve :

    /// Sample emitter function that consumes object an prints its local parameter, also assigns it to sample object value in window.
    self.webView?.evaluateJavaScript(
        "window.emitter = (sampleObject) => { window.sampleObject = sampleObject;setTimeout(function(){console.log('Hello sampleObject : ',sampleObject.name); }, 7000);}"
    ) { result, error in
        if error == nil { // this is returns correct staff
            
        }
    }
    self.webView?.evaluateJavaScript("window.emitter(\(accountString));") { result, error in
        if error == nil {
            print("parse object \(result)")
            
        }
    }

Result in window : Result of invoking emitter function with parameter object sent through swift

  • Thanks, but unfortunately the emitter won't read JSON, you need to pass a javascript object – andrey leganov Aug 25 '21 at 13:25
  • The issue you are facing is not the original one you asked in the question then. I have created a dummy emitter function which takes an object and assigns it to window plus printing its 'name' value to console. JSON is merely a superscript of a JS object as the name also is synonym for Javascript Object Notation so as long as you are not using objects or functions as value, serializing it should work. (At least in your example.) I dunno what your emitter function does though so I can't help with that unless you provide your emitter function. – deniz yalcin Aug 25 '21 at 22:17
  • I can provide the code from the [developit / mitt](https://github.com/developit/mitt) library inlined in the local index.html in next comment – andrey leganov Aug 26 '21 at 11:47
  • `!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).mitt=n()}(this,function(){return function(e){return{all:e=e||new Map,on:function(n,t){var f=e.get(n);f&&f.push(t)||e.set(n,[t])},off:function(n,t){var f=e.get(n);f&&f.splice(f.indexOf(t)>>>0,1)},emit:function(n,t){(e.get(n)||[]).slice().map(function(e){e(t)}),(e.get("*")||[]).slice().map(function(e){e(n,t)})}}}});window.emitter = mitt();` – andrey leganov Aug 26 '21 at 11:48