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?

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, but unfortunately the emitter won't read JSON, you need to pass a javascript object
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.
I can provide the code from the developit / mitt library inlined in the local index.html in next comment
!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();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.