Swift与JS的交互

时间:2023-01-24 12:00:15

Swift与JS的交互

原理

同Object-C与JS交互的大同小异,只是方法形式改变了。
首先我们需要引入iOS7.0出来的JavaScriptCore.framework

JavaScriptCore

  • JSContext是JS的执行环境。

  • JSValue是Swift或者OC与JS交互的中间媒体,可以转化为Swift对 象或者OC对象,也可以获取JS对象或方法。

  • JSExport 是Swift或者OC与JS交互的协议,构造一个类,遵循JSExport协议,实现协议的方法和初始化协议的属性,然后把Swift或OC对象与JS对象关联,我们就可以在JS环境下使用关联的对象调用之前实现的协议的方法或取得属性值或修改属性值。

  • JSManagedValue:JSValue的内存管理器。

  • JSVirtualMachine:JSVirtualMachine为JavaScript的运行提供了底层资源.

实战

Swift 与 JS 基本数据类型转换

在Swift中我们推荐使用let类声明类(引用类型),这是Swift加强了安全机制的结果。
首先初始化JS执行环境let context = JSContext(),并且为了捕获JS执行过程中得异常,我们给self.jsContext?.exceptionHandler闭包赋值。然后我们就正式开始与JS的交互,调用context.evaluateScript()方法执行任何JS代码,返回结果都是JSValue类型,使用toObject()方法转化成Swift对象,当然对象转换是一一对应的(如JS int类型应该转换成Swift Int类型)。我们还可以使用objectForKeyedSubscript()调用下角标函数(OC中我们直接使用[@"属性或者对象"])来取得JSContext执行环境的对象或者方法,也可以取得JSValue一系列的属性等。

代码如下

    //声明执行环境
    let context = JSContext();
    self.jsContext = context;
    //捕获异常
    self.jsContext?.exceptionHandler = {(jsContext:JSContext!,exception:JSValue!) ->Void in
        jsContext.exception = exception;
        print(exception);
    };
    let value = context.evaluateScript("2 + 2");
    print(value.toObject());
    //或者 区别print更简洁
    NSLog("\(value.toObject())");
    //直接定义JS数组
    context.evaluateScript("var array = ['徐海青',123]")
    //下标获取JS数组
    let arrayValue = context.objectForKeyedSubscript("array")
    if(!arrayValue.isArray){
        print("arrayValue不是数组")
        return
    }
    //两种方式获取属性
    print("arrayValue 长度\(arrayValue.objectForKeyedSubscript("length")),第一种方式获取的值\(arrayValue.valueAtIndex(0)),第二种方式获取的值\(arrayValue.objectAtIndexedSubscript(1)),第二种方式获取的长度\(arrayValue.valueForProperty("length"))");
    //两种方式设置array,越界自动扩展
    arrayValue.setObject("赵杰文", atIndexedSubscript: 4);
    arrayValue.setValue("水草草", atIndex: 6);
    //打印最后的array
    print(arrayValue);
    arrayValue.toArray()
    let array = arrayValue.toArray();
    //打印转换后的数组
    print(array);

函数调用

我们使用JS注入方法到执行环境,然后使用objectForKeyedSubscript()取出方法

    self.jsContext = JSContext();
    //JS异常处理
    self.jsContext?.exceptionHandler = { (jsContext:JSContext!,exception:JSValue!) ->Void in
        jsContext.exception = exception;
        print(exception);
    }
    /*  js 函数
        var func = function(value){
        if(value < 0) return;
        if(value === 0) return 1;
        return value * function(value - 1);
        }
    */
    //拼接JS
    let funStr = "var fun = function (value){" +
                 "if(value < 0) return;" +
                 "if(value === 0) return 1;" +
                 "return value * fun(value - 1);" +
                 "}";
    //打印插入前的函数
    print(funStr);
    //插入JS
    self.jsContext?.evaluateScript(funStr);
    //下标获取函数
    //let jsFunc = self.jsContext?.objectForKeyedSubscript("fun");
    //或者
    let jsFunc = self.jsContext?.evaluateScript("fun");
    //打印插入后函数,应该和前面一样
    print(jsFunc);
    //传入参数
   let result = jsFunc?.callWithArguments([5]);
    //打印结果
    print("result = \(result)");

使用闭包

申明Swift闭包block,通过 self.jsContext?.setObject() 赋值

    self.jsContext = JSContext();
    //JS异常处理
    self.jsContext?.exceptionHandler = { (jsContext:JSContext!,exception:JSValue!) ->Void in
        jsContext.exception = exception;
        print(exception);
    }
    //往JS注入类
    let block = {(name:String,qq:String) -> JSValue in
        //let context = JSContext.currentContext();
        let object = JSValue(newObjectInContext: self.jsContext);
        object.setObject(name, forKeyedSubscript: "name");
        object.setValue(qq, forProperty: "qq");
        return object;
    }
    print(block);
    self.jsContext?.setObject(block("xuhaiqing","1716329344"), forKeyedSubscript: "User");
    //验证是否注入成功
    let user = self.jsContext?.evaluateScript("User");
    print("user.name = \(user?.objectForKeyedSubscript("name")) and user.qq =  \(user?.valueForProperty("qq"))" )

使用JSExport 方便构造,我们强烈推荐使用这种方式与JS交互

    var user = User(name: "xuhaiqing", qq: "1716329344");
    user.jsContext = self.jsContext;
    user.webView = self.webView;
    //关联
    print(self.jsContext);
    self.jsContext?.setObject(user, forKeyedSubscript: "SwiftModel");
    print(user);
    //修改关联后的值
    self.jsContext?.evaluateScript("SwiftModel.name = 'zhaojiewen'");
    self.jsContext?.evaluateScript("SwiftModel.descriptions()");
    print(user.name);
    print(user.qq);

User 类

    //
    //  User.swift
    //  SwiftJSDemo
    //
    //  Created by haodf on 15/11/12.
    //  Copyright © 2015年 haodf. All rights reserved.
    //

    import Foundation
    import UIKit
    import JavaScriptCore
    @objc protocol UserJsToSwiftDelegate : JSExport{
    var name:(String){get set};
    var qq:(String){get set};
    func callSystemCamera();
    func showAlert(title:String,msg:String);
    func callWithDict(dic:[String:AnyObject]);
    func descriptions();
    }
    @objc class User :NSObject,UserJsToSwiftDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
var name:(String);
var qq:(String);
var webView:UIWebView?
var jsContext:JSContext?
//author 徐海青
//
//初始化
init(name:(String),qq:(String)){
    self.name = name;
    self.qq = qq;
}
//author 徐海青
//
//调用系统照相机
@objc func callSystemCamera(){
    let imagePickerViewController = UIImagePickerController();
    imagePickerViewController.delegate = self;
    UIApplication.sharedApplication().keyWindow?.addSubview(imagePickerViewController.view);
    UIApplication.sharedApplication().keyWindow?.rootViewController?.addChildViewController(imagePickerViewController);
}
//author 徐海青
//
//弹框显示提示信息
@objc func showAlert(title:String, msg: String) {
    //调用系统的alert
// let alertViewController = UIAlertController();
// UIApplication.sharedApplication().keyWindow?.addSubview(alertViewController.view);
//        UIApplication.sharedApplication().keyWindow?.rootViewController?.addChildViewController(alertViewController);
    //调用JS的alert
    let jsValue = self.jsContext?.objectForKeyedSubscript("alertFunc");
    jsValue?.callWithArguments([title,msg]);
    
}
//author 徐海青
//
//双向交互
@objc func callWithDict(dic: [String : AnyObject]) {
    print(self.jsContext);
    let funStr = "var swiftInsertIntoJsFunc = function(arg){" +
    "document.getElementById('swiftInsertIntoJsSpan').innerHTML = arg['name'];" +
    "}";
    //打印插入前的函数
    print(funStr);
    //插入JS
    self.jsContext?.evaluateScript(funStr);
    //下标获取函数
    //let jsFunc = self.jsContext?.objectForKeyedSubscript("fun");
    //或者
    let jsFunc = self.jsContext?.evaluateScript("swiftInsertIntoJsFunc");
    //let insertFunc = self.jsContext?.objectForKeyedSubscript("swiftInsertIntoJsFunc");
    print(dic);
    print(jsFunc);
    jsFunc?.callWithArguments([dic]);
}

//author 徐海青
//
//打印时显示的信息
    @objc func descriptions() {
        print("user.name = \(self.name) and user.qq = \(self.qq)")
    }

    @objc func imagePickerControllerDidCancel(picker: UIImagePickerController) {
     picker.view.removeFromSuperview();
        picker.removeFromParentViewController();
    }

}