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