使用SDK
初始化
JSBridge 的初始化,需要在配置 TMFJSBridgeConfiguration
中注册需要使用的 JS API,通过 -[TMFJSBridgeConfiguration registerFunction:withInvocationClass:]
来完成注册:
+ (instancetype)sharedConfiguration;
- (BOOL)registerFunction:(NSString *)function withInvocationClass:(Class)invocationClass;
示例代码
TMFJSBridgeConfiguration *configuration = [TMFJSBridgeConfiguration sharedConfiguration];
[configuration registerFunction:@"echo" withInvocationClass:[TMFJSBridgeInvocation_echo class]];
功能介绍
管理 H5 容器
通过加载 JSBridge 脚本来完成 Web 页面与客户端交互通讯。达到在客户端执行命令,监听客户端事件等功能。
前提条件
若要管理 H5 容器,必须先完成组件的初始化,详情请参见 初始化。
创建容器
初始化入口方法,使用一个 UIViewController
实例初始化一个 TMSJSBridge
实例:
- (instancetype)initWithWebViewController:(UIViewController<TMFJSBridgeWebViewControllerProtocol> *)webViewController;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
webViewController | UIViewController * |
实现了 TMFJSBridgeWebViewControllerProtocol 协议 UIViewController 对象,用于注入 TMFJSBridge 脚本。详见 「TMFJSBridgeWebViewControllerProtocol 协议」 |
Y |
TMFJSBridgeWebViewControllerProtocol 协议
@protocol TMFJSBridgeWebViewControllerProtocol <NSObject> @property (nonatomic, retain, readonly) __kindof UIView *tmf_webView; @property (nonatomic, retain) TMFJSBridge *tmf_JSBridge; @end
- 属性
属性 | 类型 | 描述 |
---|---|---|
tmf_webView | __kindof UIView * |
用于展示内容的界面 |
tmf_JSBridge | TMFJSBridge * |
用于处理 JS API 的 JSBridge |
- 返回值
类型 | 描述 |
---|---|
TMFJSBridge * |
新创建的 JSBridge 实例,用于处理 JS API 请求 |
关闭容器
停止 JSBridge 服务接口,用来清除当前已经注入的 TMFJSBridge 脚本,取消监听:
- (void)clearWebViewControllerAndCancel;
脚本注入
主动向当前 H5 容器注入 TMFJSBridge.js 脚本,注入成功后就可以进行 H5 容器管理:
- (void)injectJSBridgeIntoWebView:(nullable void(^)(BOOL success))completion;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
completion | Block |
注入完成回调 详见下方“completion 回调” |
N |
- completion 回调
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
success | BOOL |
是否完成脚本注入 | Y |
事件处理
可以通过下面接口判断当前的 URL 是否可以被 TMFJSBridge 解析:
- (BOOL)canHandleURL:(NSURL *)URL;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
URL | NSURL * |
需要判断是否能够解析的 URL 链接 | Y |
- 返回值
类型 | 描述 |
---|---|
BOOL |
当前链接是否可以被 TMFJSBridge 处理 |
当判断一个 URL 可以被 TMFJSBridge 解析后,可以通过下面接口来完成注入操作:
- (void)handleURL:(NSURL *)URL;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
URL | NSURL * |
需要解析的 URL 链接 | Y |
主动事件
客户端也可以通过接口主动发起事件:
- (void)notifyWithEvent:(NSString *)event parameters:(nullable NSDictionary *)parameters;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
event | NSString * |
发起的事件名称 | Y |
parameters | NSDictionary * |
发起的事件需要传递的参数,在 H5 容器中会被解析为 json。 | N |
Helper
TMFJSBridge 提供了便捷工具接口,可以通过此接口便捷注入自定义 javascript 脚本:
- (void)evaluateJavaScript:(NSString *)javaScript completionHandler:(nullable void (^)(
_Nullable id result,
NSError * _Nullable error))completionHandler;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
javaScript | NSString * |
使用字符串构成的自定义 javascript 脚本 | Y |
completionHandler | Block |
注入自定义脚本后的回调,包含错误信息等 详见下方“completionHandler 回调” |
N |
- completionHandler 回调
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
result | id |
注入自定义脚本的处理结果 | N |
error | NSError * |
注入自定义脚本的失败信息 | N |
其他属性
TMFJSBridge 提供了获取当前 URL / Controller / Cache / ContainerID / Title
等属性与方法:
- (nullable NSURL *)webViewURL; // 获取当前 H5 容器 URL 地址
属性 | 类型 | 描述 | 权限 |
---|---|---|---|
webViewController | UIViewController * |
当前 H5 容器控制器。需要实现 TMFJSBridgeWebViewControllerProtocol 协议 |
readonly |
containerID | NSUInteger |
当前 H5 容器的页面 ID。 | readonly |
cache | NSDictionary * |
当前 H5 容器的缓存。 | readonly |
属性 | 类型 | 描述 | 权限 |
---|---|---|---|
WebViewController_defaultTitle | NSString * |
当前 H5 容器控制器的默认标题。 | readwrite |
WebViewController_showsWebTitle | BOOL |
当前 H5 容器控制器是否展示标题。 | readwrite |
使用示例
管理 H5 容器的步骤:
- 创建一个实现
TMFJSBridgeWebViewControllerProtocol
协议的UIViewController
。 - 在当前
UIViewController
中实例化TMFJSBridge
实例。 - 在当前
UIViewController
中处理 URL 请求,并实现UIWebViewDelegate
协议。 - 在
UIWebView
回调方法里 注入 JSBridge.js 脚本,处理 URL 注入。
下面是创建一个拥有 TMFJSBridge 管理 H5 容器能力的 WkWebViewController
示例:
#import "TMFJSBridge.h"
// 创建一个实现了 TMFJSBridgeWebViewControllerProtocol 协议的 UIViewController
// 这里实现 WkWebViewDelegate 协议目的是为了注入脚本
@interface TMFJSBridgeWKWebViewController : UIViewController <WKUIDelegate,WKNavigationDelegate, TMFJSBridgeWebViewControllerProtocol>
@property (nonatomic, strong, readonly) WkWebView *webView;
- (instancetype)initWithURL:(NSURL *)URL;
@end
@interface TMFJSBridgeWKWebViewController ()
@property (nonatomic, strong) NSURL *URL;
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation TMFJSBridgeWKWebViewController
@synthesize tmf_JSBridge = _tmf_JSBridge;
- (instancetype)initWithURL:(NSURL *)URL
{
self = [super init];
if (self) {
// 初始化 TMFJSBridge 实例
self.URL = URL;
self.tmf_JSBridge = [[TMFJSBridge alloc] initWithWebViewController:self];
self.title = @"TMFJSBridge Demo";
}
return self;
}
- (void)dealloc
{
self.webView.delegate = nil;
// 在页面生命周期结束 关闭并清理 TMFJSBridge 服务
[self.tmf_JSBridge clearWebViewControllerAndCancel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// 创建 H5 容器 - WkWebView
WkWebView *webView = [[WkWebView alloc] initWithFrame:self.view.frame];
webView.delegate = self;
self.webView = webView;
[self.view addSubview:self.webView];
NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
[self.webView loadRequest:request];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// 主动向 H5 容器传递事件
[self.tmf_JSBridge notifyWithEvent:@"containerAppear" parameters:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// 主动向 H5 容器传递事件
[self.tmf_JSBridge notifyWithEvent:@"containerDisappear" parameters:nil];
}
#pragma mark - WkWebViewDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURLRequest *request = navigationAction.request;
// Handle the JSBridge operation if it can
if ([self.tmf_JSBridge canHandleURL:request.URL]) {
[self.tmf_JSBridge handleURL:request.URL];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
// Inject TMFJSBridge into webView
[self.tmf_JSBridge injectJSBridgeIntoWebView:nil];
}
#pragma mark - TMFJSBridgeWebViewControllerProtocol
- (UIView *)tmf_webView
{
return self.webView;
}
@end
NS_ASSUME_NONNULL_END
容器初始化完成后,通过以下方式唤起一个 TMF H5 容器:
- (void)createJSBridgeWkWebViewController
{
NSString *URLString = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"htm"];
NSURL *URL = [NSURL fileURLWithPath:URLString];
TMFJSBridgeWkWebViewController *webViewController = [[TMFJSBridgeWkWebViewController alloc] initWithURL:URL];
[self.navigationController pushViewController:webViewController animated:YES];
}
自定义 JSAPI 功能
根据自身业务需求自定义 JSAPI ,基于自定义 JSAPI 功能,在 H5 容器完成特定的需求。
前提条件
若要进行自定义 JSAPI 创建与使用,必须先完成组件初始化,详情请参见 初始化。
方法调用
JS API 的执行方法,由子类负责实现。
- (void)invokeWithParameters:(nullable NSDictionary *)parameters;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
parameters | NSDictionary * |
当前方法注入的参数 | N |
invoke 结束回调
在 invoke 完成时,子类必须调用如下方法之一来保证闭环:
// 成功 - (void)finishWithValues:(nullable NSDictionary *)values completed:(BOOL)completed; // 失败 - (void)failWithValues:(nullable NSDictionary *)values completed:(BOOL)completed;
此方法会把 invoke 的返回值回调到 Web 页面。
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
values | NSDictionary * |
完成回调参数,用于回调给 Web 页面 | N |
completed | BOOL |
是否完成 JS API 的执行: 1. 若为 YES,则会内部调用 -[TMFJSBridgeInvocation complete] 方法2. 若为 NO,在 JS API 任务结束后需要由子类主动调用 -[TMFJSBridgeInvocation complete] 方法3. 在此以前,子类仍然可以通过调用 --[TMFJSBridgeInvocation callbackWithIdentifier:values] 进行自定义回调回调自定义方法 |
Y |
注意事项
当 Web 页面调用某个 JS API 时,JSBridge 内部会执行此方法。因此,子类或其他调用者不应该调用此方法。在 invoke 完成时,子类必须调用
-[TMFJSBridgeInvocation finishWithValues:completed:]
或-[TMFJSBridgeInvocation failWithValues:completed:]
来完成功能闭环。
方法回调
此方法回调 JS API parameters 参数里的回调函数。可以通过此方法完成自定义回调。
- (void)callbackWithIdentifier:(id)identifier values:(nullable NSDictionary *)values;
- 参数
参数 | 类型 | 描述 | 必选 |
---|---|---|---|
identifier | id |
回调标识 | Y |
values | NSDictionary * |
回调给 H5 页面的参数值 | N |
注意事项:回调标识
例如,JS 执行了如下代码:
TMFJSBridge.invoke('xxx', { progress: function(args) {} })
则在 JS API 对应的 invocation 子类中,在
-[TMFJSBridgeInvocation invocationWithParameters:]
的实现中,用以下方式可以回调 progress 方法:[self callbackWithIdentifier:parameters[@"progress"] values:values];
完成方法调用
此方法用于完成 JS API 的执行。
- (void)complete;
在 invoke 的所有任务完成时,子类必须要调用此方法。JSBridge 收到 complete
通知后,将会销毁当前 invocation 实例,此后,invocation 的所有执行将停止,无法再向 Web 页面进行回调。
取消执行
此方法用于取消 JS API 的执行。
- (void)cancel;
当 Web 页面被退出时,JSBridge 内部会执行此方法。因此,子类或其他调用者不应该调用此方法。
使用示例
流程步骤:
- 建立自定义 JSAPI 的扩展类。
- 命名规范:建议采取
TMFJSBridgeInvocation_functionName
的命名方式。 - 前置条件:需要引入 "TMFJSBridgeInvocation+Protected.h" 头文件。
- 类实现:扩展类需要继承于
TMFJSBridgeInvocation
。 - 方法实现:
- [TMFJSBridgeInvocation invokeWithParameters:]
。
- 命名规范:建议采取
- 在 TMFJSAPIs_Custom.h 文件引入自定义 JSAPI 扩展类的头文件。
- 在 AppDelegate.m 文件的 prepareJSBriget 方法中注册自定义 JSAPI 。
- 命名规范:建议采取
TMFJSBridgeInvocation_functionName
中的 functionName 作为函数名注册。
- 命名规范:建议采取
- 后续使用,在 H5 页面中调用自定义的 JSAPI。
- 基于 Safari 查看调试信息,看自定义 JSAPI 是否符合预期。
下面以获取上报地理位置为例:
#import "TMFJSBridgeInvocation.h"
@interface TMFJSBridgeInvocation_reportCurrentLocation : TMFJSBridgeInvocation
@end
@implementation TMFJSBridgeInvocation_reportCurrentLocation
- (void)invokeWithParameters:(NSDictionary *)parameters {
NSMutableDictionary *returnValue = [NSMutableDictionary dictionary];
// 此处省略客户端获取地理位置的逻辑代码
NSString *location = @"中国";
if (location) {
returnValue[@"location"] = location;
}
[self completeWithReturnValue:@(0) extendedValue:returnValue];
}
- (void)cancel
{
// 中断客户端获取地理位置的异步方法
}
#ifndef TMFJSAPIs_Custom_h
#define TMFJSAPIs_Custom_h
// TMFJSAPIs_Custom 中引入头文件 (也可以自行才使用处引用)
#import "TMFJSBridgeInvocation_reportCurrentLocation.h"
#endif /* TMFJSAPIs_Custom_h */
// AppDelegate.m 中注册自定义 JSAPI
TMFJSBridgeConfiguration *configuration = [TMFJSBridgeConfiguration sharedConfiguration];
[configuration registerFunction:@"reportCurrentLocation" withInvocationClass:[TMFJSBridgeInvocation_reportCurrentLocation class]];
H5安全键盘扩充
前提条件
已经参考 管理 H5 容器 进行容器的初始化配置。
引入头文件
#import "TMFJSBridgeInvocation_safeKeyboardDisplay.h"
遵守协议
遵守<TMFSafeKeyboardWrapperWebViewControllerDeleate>
协议
@interface TMFJSBridgeWKWebViewController : QDCommonViewController <WKUIDelegate,WKNavigationDelegate, TMFJSBridgeWebViewControllerProtocol, TMFSafeKeyboardWrapperWebViewControllerDeleate>
声明属性
声明属性codeViewKeyboardWrapper
@property (nonatomic, strong) TMFSafeKeyboardWrapper *codeViewKeyboardWrapper;
初始化属性
// keyboard
self.codeViewKeyboardWrapper = [[TMFSafeKeyboardWrapper alloc] initWithKeyboard:TMFSafeKeyboard.new];
[self.codeViewKeyboardWrapper setKeyboardDelegate:self];
实现协议方法
#pragma mark - keyboard
- (BOOL)safeKeyboardShouldReturn:(TMFSafeKeyboard *)safeKeyboard{
return YES;
}
- (void)safeKeyboardUpdateViewConstraints:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardWillAppear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardDidAppear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardWillDisappear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardDidDisappear:(TMFSafeKeyboard *)safeKeyboard {}
使用示例
- 剩余代码,请参见 管理H5容器使用示例
@interface TMFJSBridgeWKWebViewController : QDCommonViewController <WKUIDelegate,WKNavigationDelegate, TMFJSBridgeWebViewControllerProtocol, TMFSafeKeyboardWrapperWebViewControllerDeleate>
@property (nonatomic, strong, readonly) TMFWkWebView *webView;
@property (nonatomic, strong) TMFSafeKeyboardWrapper *codeViewKeyboardWrapper; // 键盘设置
- (instancetype)initWithURL:(NSURL *)URL;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 安全键盘
self.codeViewKeyboardWrapper = [[TMFSafeKeyboardWrapper alloc] initWithKeyboard:TMFSafeKeyboard.new];
[self.codeViewKeyboardWrapper setKeyboardDelegate:self];
// 创建 H5 容器 - WkWebView
WkWebView *webView = [[WkWebView alloc] initWithFrame:self.view.frame];
webView.delegate = self;
self.webView = webView;
[self.view addSubview:self.webView];
NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
[self.webView loadRequest:request];
}
#pragma mark - keyboard
- (BOOL)safeKeyboardShouldReturn:(TMFSafeKeyboard *)safeKeyboard{
return YES;
}
- (void)safeKeyboardUpdateViewConstraints:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardWillAppear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardDidAppear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardWillDisappear:(TMFSafeKeyboard *)safeKeyboard {}
- (void)safeKeyboardDidDisappear:(TMFSafeKeyboard *)safeKeyboard {}