Sketch插件如何架构
原文:https://regx.vip/post/sketch-plugin/
Sketch插件跟我们平时用得比较多的Chrome插件类似,都是可以调用容器提供的API来实现各种个样的能力。
Sketch的强大之处在于,它可以调用OSX系统级别的API。这个也就是我之前的文字提到过(《用CMake打包一个Sketch插件FrameWork》),为什么需要开发Sketch插件的时候需要研发一套动态库来支持能力拓展。
本文不是针对新手Sketch插件开发者的入门,需要了解架构原理,需要一些Sketch插件开发的基础。
技术选型
native or webview
native有优势主要体现在交互体验好,webview则是学习曲线平滑,开发迭代效率快。
对比nativewebview体验好✔快速迭代✔学习曲线低✔热更新✔
作为一个产品的架构师,我们需要从3个方面去分析产品的架构生命周期:
体验
: 良好的产品体验可以帮我们提升用户的粘性。开发效率
:快速迭代抢先占领市场才能抢得先机。维护成本
:产品上线,人员流动也是市场规律,需要保证新成员能快速上项目。
UiwebView or WkWebView
webview的好处前面我们已经分析过了。具体是选项 UiwebView
还是 WkWebView
,我们也需要斟酌下。 UiwebView带给我们的好处主要是开发模式简单,同步的开发流程让架构设计更简单。
const webViewClass = {
'webView:didCreateJavaScriptContext:forFrame:': (view) => {
view.windowScriptObject().setValue_forKey(wrap({
getName() {
return 'zobor'
},
}), 'Demo');
},
}
JSAPI的用户则是这样:
window.Demo.getName();
WkWebView才是未来,苹果推出它就是为了接替UiwebView的:
- 更安全
- 支持更多新功能,比如PWA
- 性能更好
WkWebView推出Delegate模式:
const WebScriptHandlerClass = {
'userContentController:didReceiveScriptMessage:': (_, msg) => {
const jsonData = NSJSONSerialization.dataWithJSONObject_options_error(
msg.body(), 0, null,
);
const nsstring = NSString.alloc().initWithData_encoding(jsonData, NSUTF8StringEncoding);
const webParams = JSON.parse(`${nsstring}`);
console.log(webParams);
},
};
const wkwebviewConfig = WKWebViewConfiguration.alloc().init();
const messageDelegate = ObjCClass(WebScriptHandlerClass).new();
const webView = WKWebView
.alloc()
.initWithFrame_configuration(
CGRectMake(0, 0, 300, 600),
wkwebviewConfig,
);
webView.configuration()
.userContentController()
.addScriptMessageHandler_name(messageDelegate, 'DemoMessage');
然后我们的JSAPI就是这样的:
window.webkit.messageHandlers.DemoMessage.postMessage({
name: 'zobor',
});
postMessage就有个问题了,WkWebView内核该怎么回调webview呢? 我们是通过webView.evaluateJavaScript_completionHandler(script, nil);
来实现的,有点像jQuery的Ajax方法回调函数的写法。
JSAPI的设计
内核和UI交互搭建好了,接下来需要提供接口能力。 例如如先实现一下获取Sketch的文档ID的接口: api-document.js
const Sketch = require('sketch');
const DocumentAPI = {
getDocuments() {
const docs = Sketch.getDocuments();
const ret = [];
docs.forEach((doc) => {
if (doc.sketchObject) {
ret.push(doc.sketchObject);
}
});
return ret;
},
getDocument() {
return this.getDocuments()[0];
},
getDocumentID() {
return `${this.getDocument().documentData().objectID()}`;
},
};
API定义好了,接下来需要跟WKWebView的ScriptMessage提供映射
.
const DocumentAPI = require('./api-document');
const WebScriptHandlerClass = {
'userContentController:didReceiveScriptMessage:': (_, msg) => {
const jsonData = NSJSONSerialization.dataWithJSONObject_options_error(
msg.body(), 0, null,
);
const nsstring = NSString.alloc().initWithData_encoding(jsonData, NSUTF8StringEncoding);
const webParams = JSON.parse(`${nsstring}`);
console.log(webParams);
const { apiName, apiParams, callbackFn } = webParams;
const rs = DocumentAPI[apiName](apiParams);
const script = NSString.stringWithFormat('window.%@(%@)', callbackFn, rs);
webView.evaluateJavaScript_completionHandler(script, nil);
},
};
能力拓展
JS API的问题也是比较明显的,Sketch官方的JS API能力比较有限,比如我们经常会用到的curl,需要我们自己去实现。 从WKWebView到Sketch JSAPI,再到MacOs系统接口,我们的架构能力是完善了。 怎么写Sketch的插件拓展? 首先思路是通过Objective-C or Swift去编译一个FrameWork库,然后Sketch Plugin可以去Load这个库。
const loadFramework = _mocha.loadFrameworkWithName_inDirectory_('Demo', frameworkPath);
// 加载成功,会在Sketch的context上注册一个对象,对象名称是OC 定义的Class
// 例如:DMOHttp,有一个get 方法
const data = DMOHttp.get(url);
console.log(data);
总结
WKWebview+JSAPI的架构带来的好处是快速迭代 + 高效的开发效率,对此架构模式感兴趣的朋友可以联系我一起探讨.