转载

深入浅出 Fuse

在上次Fuse 15 分钟入门教程 之后,用 Fuse 开发的第一个 AppProducter 也提交到了 App Store

代码已经开源在了 Github 这篇文章将用来总结一些 Fuse 开发中的 “深坑” 和经验。

引入 Javascript 库

Fuse 的逻辑层都是由 Javascript 来处理,那么利用一些好用的库会使得我们的开发更为有效,以 Moment.js 为例,这是前端常用的一个时间解析库。

第一步就是到 http://momentjs.com 下载最新版本的库,把 moment with locales 这库存储到项目目录里,例如我存储在了 assets/js 目录下,那么在 MainView.ux 里,既可以这样载入这个模块

<JavaScript File="assets/js/moment.min.js" ux:Global="Moment"/>   

如果要使用这库,就可以在 Javascript 文件里这样 require 它,require 里的字符串要和我们 ux:Global 里写的一样

var Moment = require('Moment');   

接下来你就可以直接用 Moment 来解析时间了。

读取本地文件

在 Producter App 里我用到了 Webview 来结合 JSON 和本地的 HTML 模版显示文章,Fuse 的 Javascript 并没有接口可以读取本地文件,所以需要利用 Uno 来实现这个功能。

首先确保你的 .unoproj 里的 Includes 里有打包 *.html

  "Includes": [     "*",     "*.html"   ] 

随后将下面这段代码保存为 TextFile.uno

using Uno;   using Uno.Collections;   using Fuse;   using Uno.UX;   using Fuse.Scripting;   using Fuse.Reactive;  public class TextFile: NativeModule   {     readonly FileSource _file;      [UXConstructor]     public TextFile([UXParameter("File")] FileSource file)     {          AddMember(new NativeFunction("readSync", (NativeCallback)readSync));         _file = file;     }      object readSync(Context c, object[] args)     {         return _file.ReadAllText();     } } 

在开始我们用 using 引用了一些我们会用到的库,这些被引用的库要确保存在于 .unoproj 里,你可以参考 Producter 的配置文件

  "Packages": [         "Fuse.PushNotifications",         "Fuse.Animations",         "Fuse.BasicTheme",         "Fuse.Themes",         "Fuse.Controls",         "Fuse.Designer",         "Fuse.Drawing",         "Fuse.Drawing.Primitives",         "Fuse.Effects",         "Fuse.Elements",         "Fuse.Entities",         "Fuse.Gestures",         "Fuse.Navigation",         "Fuse.Shapes",         "Fuse.Triggers",         "Fuse.Reactive",         "Fuse.Android",         "Fuse.Desktop",         "Fuse.iOS",         "FuseCore",         "Fuse.Launcher",         "Uno.Collections",         "Uno.Geometry",         "Fuse.Scripting",         "Experimental.iOS",         "ObjC"   ] 

TextFile 这个类后面还有一个 NativeModule 继承,通过这个继承就使得我们这个类可以通过 <TextFile /> 的形式放在 Mainview.ux 里,所有自定义的类都需要用这样的方式加载到我们的 Fuse App 里。

更详细的 NativeModule 使用细节可以参考官方的 Guide Working with Uno Code

UXConstructor 字段的修饰让 <TextFile /> 在创建这个类的时候知道 <TextFile File="xxxxx"/> 对应 [UXParameter("File")] FileSource file 传递构造参数给这个类的构造方法。

readSync 方法里,通过 ReadAllText() 读取了路径文本,那么接下来还有一件事情就是要把这个方法暴露给 Javascript。

通过 AddMember(new NativeFunction("readSync", (NativeCallback)readSync)); 我们可以让 Uno 完成方法到 Javascript 的桥接。

完成这两步之后,在 Mainview.ux 加入文件的引用

<TextFile File="videoHTMLTemplate.html" ux:Global="videoHTMLTemplate" />   

接下来就可以在 Javascript 里使用这个文件了

var videoHTMLTemplate = require("videoHTMLTemplate");   // Read Templates var videoHTMLTemplateString = videoHTMLTemplate.readSync();   

最后 Webview 通过 Source 属性将我们自己生成好的 HTML 填充进去, presentedArticleHTML 绑定到了我们自己生成的 HTML 数据上。

<WebView Source="{presentedArticleHTML}" >   </WebView>   

调用 iOS API

在后续的版本会舍弃现在的调用方法,引入 Foreigon Code 的模式,不过官方说还有几周的时间,所以这个即将过时的方法就先帖出来吧。

这种模式依赖于 Uno,Uno 一面把接口暴露给 Javascript,一面去调用 iOS 的接口。

具体的例子可以参考 Producter 的 FuseStoreKit.uno 这个文件,里面的用法已经相当深入,触及了 API 调用的瓶颈,这也正是为什么 Fuse 接下来要弃用 Uno 桥接的原因。

如果需要引用某些 iOS 的库,确保 .unoproj 已经引入了 ObjcExperimental.iOS 这两个库,不确定话可以直接照搬 Producter 的配置文件。

例如我们需要引用 iOS 的 StoreKit 库,那么可以用下面的语句

using global::iOS.StoreKit;   

因为我们的 NativeModule 需要同时存在于 iOS 和 Android,所以 FuseStoreKit 其实是我们实际接口操作类的上一层代理,避免直接调用发生悲剧。

public class FuseStoreKit : NativeModule   {    extern(iOS)   StoreKit storeKit = new StoreKit();    extern(iOS)   Storage storage = new Storage();    extern(iOS)   CloudKit cloudKit = new CloudKit(); } 

extern 表示仅当是 iOS 平台的时候才会创建下面的属性。

而具体的方法在执行的时候,也用了 defined 来确保只有在正确的平台上才会执行,如果不是这个平台,Javascript 在调用的时候,就会 null。

  object MakeSubscribe(Context c, object[] args) {     if defined (iOS) {       Subscribe();     }     return null;    } 

具体创建 iOS 类的时候,在 Uno 里写起来没有 iOS 那么得心应手,init 方法和类方法都有着不同的调用规则,例如创建 NSUserDefaults 的时候,要这样去写

NSUserDefaults userDefaults = new NSUserDefaults(NSUserDefaults._standardUserDefaults());   

NSUserDefaultsstandardUserDefaults 是一个类方法,因此要加下划线,然后通过 new NSUserDefaults() 来包住这个方法去创建。

如果是 init 方法,那么使用起来就更麻烦一些,先初始化,然后再 init。

NSString begin = new NSString();   begin.initWithString("producter_month_subscribe");   

奇技淫巧参考 FuseStoreKit.uno 中我注释掉的代码。

Delegate

有些需要注意的是, .delegate = this 这种直接的属性赋值通常是不能工作的,需要使用 request.setDelegate(this); 这种方法。

其次是 Objective C 的方法如果有多个参数一般是成段隔开的,在 Uno 里要合成一段然后再在括号里分别传入参数,例如以下这段

- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error   

那么在 Uno 里就要这样写

public void paymentQueueRestoreCompletedTransactionsFailedWithError (SKPaymentQueue queue, NSError error)   {     // Restore failed somewhere... } 

目前 Objc 的闭包回调函数在 Uno 里还没实现,有个叫 UXL 的可以做,但是…… 我们还是等 Foreigon Code 吧。

正文到此结束
Loading...