HTML5 App 相比 Native App 有一些突出的优势,但有时候我们希望能够把这些 HTML5 App 封装为 iOS Native App。

本文描述了一种最简单的方案,演示了如何将一个 PixiJS 的 HTML5 App 封装为 iOS Native App。

原理

从 iOS 8 开始,Apple 为了让嵌入 HTML5 的应用程序能够获得更好的性能表现,提供了一个新的系统组件 WKWebView。这个 WKWebView 与以前的 UIWebView 相比,最大的改变就是支持 iOS 的高性能 JavaScript 虚拟机,所以在性能上可以保持和 iOS Safari 浏览器同样的表现。

我们的原生封装就是在一个 iOS Native App 里使用 WKWebView 来跑 PixiJS。

但光有 WKWebView 还不行,因为 WKWebView 里因为安全限制,是不能直接读取 App 内部的资源和脚本的。所以我们还需要在 App 里嵌入一个 HTTP Server。

幸运的是已经有开发者创建了一个可以嵌入 iOS Native App 的 HTTP Server 组件。这个组件名为 GCDWebServer,项目地址是 https://github.com/swisspol/GCDWebServer

GCDWebServer 简单易用,占用的资源也极少,非常符合我们的需求。

所以我们的基本思路就是:

  • 在 iOS Native App 里创建一个 HTTP Server。
  • 再创建一个 WKWebView
  • WKWebView 从内部的 HTTP Server 读取内容并执行,从而把我们的 HTML5 内容跑起来。

~

创建 iOS Native App

打开 Xcode,选择新建一个 iOS Single View Application,命名为 PixiJS Native

https://github.com/swisspol/GCDWebServer/releases 下载 GCDWebServer 的最新版本并解压缩。

将解压缩得到的 GCDWebServer 目录复制到新建项目里,并添加到工程中。完成后应该如下图:

添加 GCDWebServer 文件到工程中

并且在工程中添加下列 Frameworks:

  • libxml2.tbd
  • libz.tbd
添加编译工程需要的 Frameworks

确保工程可以正常编译。

~

添加 Native 代码

打开 ViewController.m,增加:

#import "WebKit/WebKit.h"
#import "GCDWebServer/Core/GCDWebServer.h"
#define LOCAL_PORT 29033

修改 viewDidLoad 方法为:

- (void)viewDidLoad {
[super viewDidLoad];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:webView];
id basedir = [NSString stringWithFormat:@"%@/src",
[[NSBundle mainBundle] resourcePath]];
GCDWebServer* webServer = [[GCDWebServer alloc] init];
[webServer addGETHandlerForBasePath:@"/"
directoryPath:basedir
indexFilename:@"index.html"
cacheAge:0
allowRangeRequests:YES];
//DEBUG = 0
//VERBOSE = 1
//INFO = 2
//WARNING = 3
//ERROR = 4
[GCDWebServer setLogLevel:0];
id options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithInteger:LOCAL_PORT]
forKey:GCDWebServerOption_Port];
[options setObject:[NSNumber numberWithBool:YES]
forKey:GCDWebServerOption_BindToLocalhost];
[webServer startWithOptions:options error:nil];
id urlStr = [NSString stringWithFormat:@"http://127.0.0.1:%d/", LOCAL_PORT];
id url = [NSURL URLWithString:urlStr];
id request = [[NSURLRequest alloc] initWithURL:url];
[webView loadRequest:request];
}

这一段代码分为几个部分:

  1. 创建一个 WKWebView,并且填满整个显示空间。
  2. 将 App 里的 src 目录作为 HTML5 内容的根目录。
  3. 创建一个 GCDWebServer 对象实例,并设置根目录、默认文件、缓存策略等。这里 cacheAge 设置为 0 是让每次应用启动都可以确保读取到最新的内容。
  4. 让 WebServer 监听本机的指定端口号,这样从外部是无法访问 WebServer 的,确保了安全。
  5. 最后,让 WKWebView 访问本机 WebServer 的地址。

~

添加 HTML5 内容

在项目目录里新建一个 src 目录,放入要运行的 HTML5 内容。

然后将 src 目录拖动到 Xcode 工程中,并且选择 Create folder references

添加 src 目录为引用

添加完成后,src 目录在 Xcode 工程里的图标应该是蓝色的。

如果一切顺利,此时执行工程应该就可以在模拟器或者 iPhone 上看到我们的 HTML5 内容了。

运行效果

~

总结

本文提供的方案简单易行,但也存在一个主要的问题。那就是运行在 Native App 里的 HTML5 内容无法访问一些 iOS 原生接口,例如 IAP。

这个问题的解决方案:

  • 自己对这些 iOS 原生接口封装为 JavaScript 接口,给 HTML5 内容调用。
  • 使用 PhoneGap 等现成的框架,它们已经封装了大部分常用的 iOS 原生接口。

最后,完整的工程可以从 https://github.com/dualface/wrap-html5-ios-native 下载。

-EOF-