本文介绍接入 iOS 过程中常见的问题及相应的解决方案。
注意事项
H5 容器初始化时,最好指定所有 H5 页面的基类和 WebView 的基类,便于统一处理。
前端在开发离线包时,对应路径及文件名中不允许有中文字符,否则会导致客户端离线包解压失败。
离线包中各资源路径的绝对长度不要超过 100 字符,否则导致客户端 tar 包解压失败,页面白屏。
如何解决 H5 容器定位偏移问题
解答:在使用 mPaaS 容器的过程中可能会遇到 H5 容器定位偏移的问题,请参考以下方法进行设置更新:
- (void)application:(UIApplication *)application beforeDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//跳过 LBS 定位
[LBSmPaaSAdaptor sharedInstance].shouldSkipLBSLocation = YES;
.......
}
预置离线包使用 H5_json.json 文件不生效
解答:在 10.1.32 基线中,只支持 plist 格式。在 10.1.60 基线中,plist 和 JSON 格式都支持。
如何获取已安装的离线包应用信息
解答:参考代码 NSDictionary *installedApps = [NAMServiceGet() installApps:nil];
。
如何强制更新全部离线包信息
解答:可以使用封装的 requestAllNebulaApps
方法进行全量更新。
[[MPNebulaAdapterInterface shareInstance]requestAllNebulaApps:^(NSDictionary *data, NSError *error) {
}];
在 iOS 13 中的 vux-ui pulldown 组件在 H5 容器中滑动卡死时如何处理
解答:这是因为触发了 UIWebview
在 iOS 系统中的 bug,可以考虑切换成 WKWebview 或更换前端组件。切换为 WKWebview 的方法请参考文档 mPaaS 10.1.60 适配 WKWebView。
如何查看当前应用已注册的所有 JSAPI 和 Plugins
解答:打开一个 H5 页面,进入 Xcode View 层级页面,在 lldb 控制台使用 po [[PSDService sharedInstance] jsApis]
查看所有 JSAPI。同理使用 po [[PSDService sharedInstance] plugins]
查看所有注册的 Plugins。
如何在 JSAPI 或 Plugin 中获取当前 H5 页面的 UIViewController 和 Webview 对象
解答:在实际执行过程中,Plugin 可直接拿到参数 event.context
,JSAPI 中可获取到 context
,在上下文 PSDContext
对象中,可以取到您想要的所有信息或引用,比如当前控制器 event.currentViewController
的引用,当前 WebView 的引用 context.currentViewController.psdContentView
等等。
如何获取当前页面的 WebView
解答:当前 H5 页面的 WebView 是当前 VC 的一个属性,可通过 vc.psdContentView
获取,在 JSAPI 或 Plugin 中可通过上面的方法获取当前页面的 VC。
在 H5 页面基类获取当前页面的 WebView 时,请在 viewWillAppear 方法中。viewDidLoad 方法中 WebView 未创建,如果使用该方法,取到的值会为 nil。
如何获取当前页面加载时前端传入的启动参数
解答:直接获取当前 VC 的 psdScene.createParam.expandParams
属性。
如何限制 JSAPI 只在某个特定离线包中起作用
解答:在 JSAPI 的实际执行方法中,获取当前页面所属离线包的 appId,决定是否执行逻辑即可。
如何拦截页面 URL
解答:您可以自定义 Plugin,通过监听事件来实现。
监听事件名称
[PSDProxy addEventListener:kEvent_Proxy_Request_Start_Handler withListener:self useCapture:YES];
。拦截处理。
else if ([kEvent_Proxy_Request_Start_Handler isEqualToString:event.eventType]
&& [event isKindOfClass:[PSDProxyEvent class]] ){
NSLog(@"-----kNBEvent_Scene_NavigationItem_Right_Setting_Click----");
PSDProxyEvent *proxyEvent = (PSDProxyEvent*) event;
NSMutableURLRequest *redirectReq = proxyEvent.request.mutableCopy;
NSString *appId = event.context.currentSession.createParam.expandParams[@"appId"];
NAMApp *app = [NAMServiceGet() findApp:appId version:nil];
NSString *fallBackUrl = app.fallback_host;
NSString *vhost = app.vhost;;
NSString *url = redirectReq.URL.absoluteString;
NSLog(@"url__%@______fallBackUrl:%@",url,fallBackUrl);
if ([url containsString:@"www.baidu.com"]) {
[proxyEvent preventDefault];
}}
如何在当前 H5 页面加载前,拦截当前页面 URL
解答:在 H5 页面的基类中,实现 UIWebView 的生命周期的代理方法中,监听 kEvent_Navigation_Start
事件,在页面加载之前拦截,获取当前页面的 WebView 和 URL 进行相关处理。
如何在 Native 手动调用 JSAPI
解答:有时 Native 端可能需要您在当前页面手动调用某个 JSAPI 接口,可通过调用当前 VC 的以下接口实现。
为什么本地预置的离线包加载不生效
解答:预置资源包加载失败一般为预置包版本和包信息不匹配,测试本地预置离线包时,请先断开网络,避免离线包有更新,确保加载的是客户端本地预置的版本。
检查本地预置的离线包信息与 Plist 中配置的包信息是否一致,包括 app_id、version、main_url 等信息。
为什么控制台发布新版本离线包后客户端不能正常加载到新包
解答:在查看此问题解决方案前,确认您已理解 离线包更新原理,客户端不能正常加载新包,离线包渲染在任一阶段都有可能出错,下面将一一进行排查:
查看全量更新离线包的 RPC 返回结果,在控制台搜索“bizType: 4”确认返回的离线包详情,确认已经拉取了控制台发布的最新包信息。
上一步检查通过后,在加载离线包的 finish 回调方法里查看离线包信息,确保为控制台发布的最新包,且 error 的值为 nil,检查离线包的 app_id、version、main_url 等是否正确。
上一步检查通过后,查看沙盒目录下离线包是否解压成功(若当前离线包有引用全局资源包中内容,此解压目录下同样需要有全局资源包)。
若上一步中沙盒目录下无对应离线包,可先暂时关闭离线包验签,删除 App 重新运行。若关闭验签后加载正常,说明离线包加签私钥和客户端验签公钥不一致,请更新客户端对应的公钥信息。
若上述步骤检查都通过,离线包更新仍失败,可用 Safari 调试对应 H5 页面,具体查看报错原因。如全局资源包路径不正确,离线包加载的 URL 不存在等。
全局资源包加载失败
解答:当通过 Safari 调试判断出全局资源包加载失败时,可按下述步骤具体排查:
检查全局资源包是否注册。
上一步检查通过后,查看沙盒目录下离线包是否解压成功。
检查引用的全局资源包中资源文件路径是否正常,且保证引用路径中无中文字符。
为什么打开某个离线包 H5 页面会白屏或出现 400 错误
解答:页面白屏或出现 400 错误,一般为本地离线包加载失败导致使用了在线 fallback 地址,而对应页面的 fallback 地址不存在,导致页面加载失败。
按上述离线包问题排查客户端离线包加载失败的原因。
针对加载在线 fallback 地址失败的问题,确认对应离线包版本生成正确,且已在控制台已上传,包括普通离线包和全局资源包,具体请参考 生成离线包。
预置离线包若出现在线 fallback 地址失败的问题,先保证预置的离线包在控制台同样也上传。
保证本地预置包信息中
fallback_base_url
与从控制台下载的 h5_json.json 配置文件中fallback_base_url
一致。并且
fallback_base_url + main_url
拼接的地址在浏览器上可正常加载。
怎样禁止 H5 页面的手势侧滑返回功能
解答:支持由前端 H5 页面禁止和原生 H5 容器基类禁止。
前端 H5 页面禁止:调用 setGestureBack JSAPI 实现。适用于某一个页面需要禁止手势侧滑返回的场景
AlipayJSBridge.call('setGestureBack',{val:false});
。原生 H5 容器基类禁止:在基类的 viewDidAppear 方法中调用系统禁止侧滑返回的接口,同时设置 guestBack 参数。适用于多个或所有 H5 页面需要禁止手势侧滑返回的场景。
-(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.options.gestureBack = NO; if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; } }
如何判断当前页面是小程序中的页面
解答:获取当前页面所在的 session,调用 isTinyAppWithSession
接口判断。代码示例如下:
PSDSession *session = self.psdSession;
BOOL isTinyApp = [NBUtils isTinyAppWithSession:session];
H5 页面如何传递自定义参数
解答:根据传参方式,分为以下几种场景:
原生 - H5:调用
startH5ViewControllerWithParams
方法时传递[[MPNebulaAdapterInterface shareInstance] startH5ViewControllerWithParams:@{@"url": @"https://tech.antfin.com", @"key1":@"value1"}];
。原生 - 离线包:调用
startH5ViewControllerWithNebulaApp
方法时传递[[MPNebulaAdapterInterface shareInstance] startH5ViewControllerWithNebulaApp:@{@"appId":@"70000000",@"param":@{@"key2":@"value2"}}];
。H5 - H5:调用
pushWindow
时将自定义参数写在passData
中。AlipayJSBridge.call('pushWindow', { // 要打开页面的 URL url: 'https://m.taobao.com/', // 打开页面的配置参数 param: { readTitle: true, //自动读取 title showOptionMenu: false, // 隐藏右边菜单 transparentTitle:'always', }, // 用于给新开的页面传递参数,可选 // 在新开的页面使用 AlipayJSBridge.startupParams 可以获取到 passData passData: { key1: "key1Value", key2: "key2Value" } });
H5 - 离线包:调用 startApp JSAPI 时将自定义参数写在 param 中。
AlipayJSBridge.call('startApp', { appId: '70000000', param: { key1:'value1' } }, function(result) { // noop });
如何在 H5 页面获取传递的参数
解答:分为前端获取和原生获取两种场景:
前端获取:通过
startupParams
方法获取当前页面的启动参数。AlipayJSBridge.startupParams
原生获取:通过当前页面所在的 VC 对象获取。
// 当前页面的启动参数 NSDictionary *expandParams = self.psdScene.createParam.expandParams; NSLog(@"[mpaas] expandParams: %@", expandParams);
如何拦截 JSAPI 调用
解答:可以自定义 Plugin,通过监听事件来实现。
监听事件名称:
kEvent_Invocation_Event_Start
。拦截处理:获取到 JSAPI 的名称、传递的参数等。
else if([kEvent_Invocation_Event_Start isEqualToString:event.eventType]) { PSDInvocationEvent * invocationEvent = (PSDInvocationEvent *)event; NSString * apiName = invocationEvent.invocationName; if([apiName isEqualToString:@"setOptionMenu"] || [apiName isEqualToString:@"showOptionMenu"] ) { NSLog(@"wwww"); } }