記錄Swift和Javascript如何進行交互javascript
今天在網上看到了一篇介紹Swift和Javascript交互的文章,感受做者寫的很好,所以把做者文章中的主要知識點進行一個總結。html
對於我我的而言,在項目中使用Javascript的緣由有兩個:java
能夠再這裏下載演示demoios
demo中咱們主要演示了3大塊Swift和Javascript交互的神奇魔法:git
效果圖:github
Model,Initial OS,Latest OS,Image URL iPhone (1st Generation),iPhone OS 1.0,iPhone OS 3.1.3,https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/IPhone_2G_PSD_Mock.png/81px-IPhone_2G_PSD_Mock.png iPhone 3G,iPhone OS 2.0,iOS 4.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/IPhone_PSD_White_3G.png/81px-IPhone_PSD_White_3G.png iPhone 3GS,iPhone OS 3.0,iOS 6.1.6,https://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/IPhone_PSD_White_3G.png/81px-IPhone_PSD_White_3G.png iPhone 4,iOS 4.0,iOS 7.1.2,https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/IPhone_4_Mock_No_Shadow_PSD.png/81px-IPhone_4_Mock_No_Shadow_PSD.png iPhone 4S,iOS 5.0,iOS 9.3.5,https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/IPhone_4S_No_shadow.png/99px-IPhone_4S_No_shadow.png iPhone 5,iOS 6.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/IPhone_5.png/99px-IPhone_5.png iPhone 5C,iOS 7.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/IPhone_5C_%28blue%29.svg/88px-IPhone_5C_%28blue%29.svg.png iPhone 5S,iOS 7.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/IPhone_5s.png/88px-IPhone_5s.png iPhone 6,iOS 8.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/0/01/IPhone6_silver_frontface.png/100px-IPhone6_silver_frontface.png iPhone 6 Plus,iOS 8.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/IPhone_6_Plus_Space_Gray.svg/120px-IPhone_6_Plus_Space_Gray.svg.png iPhone 6S,iOS 9.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/IPhone_6S_Rose_Gold.png/105px-IPhone_6S_Rose_Gold.png iPhone 6S Plus,iOS 9.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/IPhone_6S_Rose_Gold.png/125px-IPhone_6S_Rose_Gold.png iPhone SE,iOS 9.3,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/en/thumb/d/d0/IPhone_SE_%28rose_gold%29.png/95px-IPhone_SE_%28rose_gold%29.png iPhone 7,iOS 10.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/IPhone_7_Jet_Black.svg/105px-IPhone_7_Jet_Black.svg.png iPhone 7 Plus,iOS 10.0,iOS 10.2.1,https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/IPhone_7_Plus_Jet_Black.svg/125px-IPhone_7_Plus_Jet_Black.svg.png
把上邊的數據解析後,展現爲:
swift
JavaScriptCore 中最主要的角色就是 JSContext 類。一個 JSContext 對象是位於 JavaScript 環境和本地 Javascript 腳本之間的橋樑。數組
所以須要初始化一個JSContext對象:markdown
var jsContext: JSContext!
我不會像原文那樣一步一步的演示功能,我只是記錄下使用JSContext的核心思想和用法。架構
咱們看看JSContext的初始化方法:
func initializeJS() { self.jsContext = JSContext() /// Catch exception self.jsContext.exceptionHandler = { context, exception in if let ex = exception { print("JS exception: " + ex.toString()) } } let jsPath = Bundle.main.path(forResource: "jssource", ofType: "js") if let path = jsPath { do { let jsSourceContents = try String(contentsOfFile: path) jsContext.evaluateScript(jsSourceContents) } catch let ex { print(ex.localizedDescription) } } // Configurate log let consoleLogObject = unsafeBitCast(self.consoleLog, to: AnyObject.self) jsContext.setObject(consoleLogObject, forKeyedSubscript: "consoleLog" as (NSCopying & NSObjectProtocol)) jsContext.evaluateScript("consoleLog") }
上邊的代碼中作了下邊這幾件事:
let consoleLogObject = unsafeBitCast(self.consoleLog, to: AnyObject.self)
unsafeBitCast用做強制類型轉換,使用的時候須要明確的知道要轉換的類型open func setObject(_ object: Any!, forKeyedSubscript key: (NSCopying & NSObjectProtocol)!)
經過這種方式爲Javascript添加屬性或者函數那麼,接下來,咱們看一段Swift中獲取Javascript屬性的代碼:
func helloWorld() { if let valiableHW = jsContext.objectForKeyedSubscript("helloWorld") { print(valiableHW.toString()) } }
由上邊的代碼能夠看出,經過函數open func objectForKeyedSubscript(_ key: Any!) -> JSValue!
能夠獲取JSValue,而後使用toString()
獲取字符串。
除了獲取屬性外,下邊的代碼演示瞭如何使用Javascript中的函數:
func jsDemo1() { let firstName = "zhang" let lastName = "san" if let funcFullName = jsContext.objectForKeyedSubscript("getFullName") { if let fullName = funcFullName.call(withArguments: [firstName, lastName]) { print(fullName) } } }
經過函數open func objectForKeyedSubscript(_ key: Any!) -> JSValue!
能夠獲取JSValue,而後調用call函數,並傳遞參數過去就實現了這個功能。
咱們在看看js代碼中是如何使用Swift屬性和函數的:
function generateLuckyNumbers() { consoleLog("打印東東啊"); var luckyNumbers = []; while (luckyNumbers.length != 6) { var randomNumber = Math.floor((Math.random() * 50) + 1); if (!luckyNumbers.includes(randomNumber)) { luckyNumbers.push(randomNumber); } } handleLuckyNumbers(luckyNumbers); }
上邊代碼中的handleLuckyNumbers函數就是Swift中的函數,你們能夠去demo中查看。
這個文本轉換最核心的內容就是解析Markdown的語法,而後輸出HTML文本,若是咱們本身手寫轉換代碼,那就太麻煩了。Javascript已經有一個很強大的第三方庫Snowdown。
在JSContext的初始化方法中添加下邊的代碼:
// Fetch and evaluate the Snowdown script. let snowdownScript = try String(contentsOf: URL(string: "https://cdn.rawgit.com/showdownjs/showdown/1.6.3/dist/showdown.min.js")!) self.jsContext.evaluateScript(snowdownScript)
上邊的代碼中把轉換腳本調入Javascript運行時,而後咱們再經過下邊的代碼調用Javascript的代碼:
func convertMarkdownToHTML() { if let funcConvertMarkdownToHTML = jsContext.objectForKeyedSubscript("convertMarkdownToHTML") { funcConvertMarkdownToHTML.call(withArguments: [self.tvEditor.text]) } }
Javascript的代碼以下:
function convertMarkdownToHTML(source) { var converter = new showdown.Converter(); var htmlResult = converter.makeHtml(source); consoleLog(htmlResult); }
核心思想就是接受Javascript轉換後的結果。
前面,咱們學習瞭如何暴露 Swift 程序代碼給 JS,但 JavaScriptCore 的功能並不只限於此。它還提供一種暴露自定義類的機制,並直接在 JS 中使用這些類的屬性和函式。這就是 JSExport,它是一個協議,經過它你可以以更強大的方式來溝通 Swift 和 JS。
咱們看看自定義類的代碼:
import UIKit import JavaScriptCore @objc protocol DeviceInfoJSExport: JSExport { var model: String! { get set} var initialOS: String! { get set} var latestOS: String! { get set} var imageURL: String! { get set} static func initializeDevice(withModel: String) -> DeviceInfo } class DeviceInfo: NSObject, DeviceInfoJSExport { var model: String! var initialOS: String! var latestOS: String! var imageURL: String! init(withModel model: String) { super.init() self.model = model } class func initializeDevice(withModel: String) -> DeviceInfo { return DeviceInfo(withModel: withModel) } func concatOS() -> String { if let initial = initialOS { if let latest = latestOS { return initial + "-" + latest } } return "" } }
若是咱們實現了JSExport協議,那麼 JavaScript 運行時就能捕獲該協議中的內容。對於這種設計,可讓咱們很靈活的使用它的功能。
再看看Javascript中關於這一段的核心代碼:
function parseiPhoneList(originalData) { var results = Papa.parse(originalData, { header: true }); if (results.data) { var deviceData = []; for (var i=0; i < results.data.length; i++) { var model = results.data[i]["Model"]; var deviceInfo = DeviceInfo.initializeDeviceWithModel(model); deviceInfo.initialOS = results.data[i]["Initial OS"]; deviceInfo.latestOS = results.data[i]["Latest OS"]; deviceInfo.imageURL = results.data[i]["Image URL"]; deviceData.push(deviceInfo); } return deviceData; } return null; }
上邊的代碼,調用了第三方解析庫的函數,把數據解析出來後,生成deviceInfo數組,而後咱們在Swift中就獲取到了解析好的數據:
func parseDeviceData() { if let path = Bundle.main.path(forResource: "iPhone_List", ofType: "csv") { do { let contents = try String(contentsOfFile: path) if let functionParseiPhoneList = self.jsContext.objectForKeyedSubscript("parseiPhoneList") { if let parsedDeviceData = functionParseiPhoneList.call(withArguments: [contents]).toArray() as? [DeviceInfo] { self.deviceInfo = parsedDeviceData self.tblDeviceList.reloadData() } } } catch { print(error.localizedDescription) } } }
實現這些功能的基礎就是Javascript的函數有返回值。
在ios7以前咱們只能經過UIWebview才能調用Javascript代碼,如今,咱們經過JavascriptCore能夠自由使用Javascript。但在使用的時候要特別注意內存管理問題,大概須要注意一下兩點:
可使用JSManagedValue去解決這個問題。
Using JavaScript in Swift Projects: Building a Markdown to HTML Editor