本文介紹接入 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"); } }