Swift与JS的交互

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();
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容