Universal Link Fix Bug

Tip:發(fā)現(xiàn)在真機(jī)運(yùn)行中有觸發(fā)handoff功能的案例,只需要在代理方法中適當(dāng)返回NO即可。

關(guān)于iOS Universal Link 的資料不勝枚舉,關(guān)于基礎(chǔ)配置這里就不再詳述。這里主要分享一下,解決其中一個(gè)坑位的過(guò)程...

之前在配置好通用連接后,覺(jué)得完事大吉。然而過(guò)了一段時(shí)間,產(chǎn)品同事來(lái)找茬咯~~~ 
話說(shuō),沒(méi)想到通用鏈接也有失效這樣一回事。萬(wàn)惡的蘋(píng)果爸爸~~~

基于這個(gè)問(wèn)題的描述如下:
iOS里面有個(gè)情況,打開(kāi)APP的后在右上角形如:
xxx.com(面包屑導(dǎo)航)跳轉(zhuǎn)到safari后,會(huì)導(dǎo)致Universal  Link 跳轉(zhuǎn)失效。

在認(rèn)識(shí)這個(gè)問(wèn)題的時(shí)候,首先需要考慮的是系統(tǒng)版本因素。發(fā)現(xiàn)在iOS11上通過(guò)通用鏈接啟動(dòng)APP后,右上角并沒(méi)有發(fā)現(xiàn)面包屑導(dǎo)航。而在iOS10和iOS9 上會(huì)出現(xiàn)此問(wèn)題,所以問(wèn)題明朗了一點(diǎn)。

iOS11  下系統(tǒng)機(jī)制有所更改,所以本篇命題不會(huì)成立。
iOS10  以下,系統(tǒng)機(jī)制還不太成熟,這也是本篇命題存在的意義。

能夠讓產(chǎn)品大大樂(lè)此不疲的一定是,某某某家的APP怎么就沒(méi)問(wèn)題呢。所以我們不服氣的研究了一下某某某家的APP。這里以兩個(gè)具有代表性的為例。

eg:在微信中,跳轉(zhuǎn)APP...  
今日頭條: 兩顆星
    表現(xiàn):呼起APP后,點(diǎn)擊右上角面包屑導(dǎo)航。會(huì)發(fā)現(xiàn)界面是向右側(cè)跳轉(zhuǎn)到Safari,這一點(diǎn)很像是使用 openURL 實(shí)現(xiàn)的效果。
網(wǎng)易:    一顆星
    表現(xiàn):呼起APP后,點(diǎn)擊右上角面包屑導(dǎo)航。WTF!!! 不可點(diǎn),這個(gè)解決方案就有點(diǎn)雞賊了。

Universal Link:失效的表現(xiàn),點(diǎn)擊右上角面包屑導(dǎo)航會(huì)發(fā)現(xiàn)頁(yè)面是向左側(cè)pop到Safari。
向左走還是向右走,簡(jiǎn)直是人生難題。

對(duì)比以上的結(jié)論:
問(wèn)題根源,面包屑導(dǎo)航打開(kāi)的是配置好的通用鏈接。這會(huì)讓系統(tǒng)誤以為,我們以后都會(huì)使用Safari打開(kāi)通用鏈接從而導(dǎo)致失效。所以解決問(wèn)題的思路一定是要在,面包屑導(dǎo)航上做文章了。

能想到的方案如下:
獲取采用從狀態(tài)欄獲取網(wǎng)絡(luò)狀態(tài)的類(lèi)似方法,來(lái)獲取面包屑導(dǎo)航的相關(guān)狀態(tài)。從而問(wèn)題的解決可以分化為兩個(gè)方案。

1.在狀態(tài)欄上,貼一層UIView 覆蓋住面包屑導(dǎo)航。這樣的好處是,iOS9--iOS11都可以獲得一致的體驗(yàn)。難點(diǎn)是要在KeyWindow的最上層添加,否則容易破相。
2.獲取面包屑導(dǎo)航的觸發(fā)事件,使用運(yùn)行時(shí)替換事件。主要目的是,為我們手動(dòng)調(diào)用openURL 方法提供便利,并可以修改跳轉(zhuǎn)鏈接為普通鏈接。

我這里采用了第二套方案,詳細(xì)過(guò)程如下:
抓取面包屑導(dǎo)航:

UIApplication *application = [UIApplication sharedApplication];
NSArray* arrChilden;
arrChilden = [[[application valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];

//查看subView 這個(gè)是沒(méi)有面包屑導(dǎo)航時(shí)的情況 
_subviewCache   __NSArrayM *    @"4 elements"   0x000060000065f6e0
[0] UIStatusBarServiceItemView *    0x7fa4eac8d310  0x00007fa4eac8d310
[1] UIStatusBarDataNetworkItemView *    0x7fa4ead01ec0  0x00007fa4ead01ec0
[2] UIStatusBarBatteryItemView *    0x7fa4ead1b070  0x00007fa4ead1b070
[3] UIStatusBarTimeItemView *   0x7fa4eae76ad0  0x00007fa4eae76ad0

//查看subView 這個(gè)是有面包屑導(dǎo)航時(shí)的情況
_subviewCache   __NSArrayM *    @"7 elements"   0x1562a850
[0] UIStatusBarBreadcrumbItemView * 0x16df8ff0
[1] UIStatusBarServiceItemView *    0x16875600
[2] UIStatusBarDataNetworkItemView *    0x16b73cd0
[3] UIStatusBarOpenInSafariItemView *   0x15556580
[4] UIStatusBarBatteryItemView *    0x16b594f0
[5] UIStatusBarBatteryPercentItemView * 0x169e0af0
[6] UIStatusBarTimeItemView *   0x16997bb0

其中 UIStatusBarBreadcrumbItemView 就是面包屑左導(dǎo)航
    UIStatusBarOpenInSafariItemView 就是面包屑右導(dǎo)航
接下來(lái)深挖 UIStatusBarOpenInSafariItemView,一下就不詳細(xì)說(shuō)了。其中有一個(gè)事件是我們所關(guān)心的。
SEL: userDidActivateButton:
至此,入手點(diǎn)有了著落。

Final Result:
AppDelegate中修復(fù)方法如下:

///注意 - (void)applicationDidBecomeActive:(UIApplication*)application 方法中調(diào)用)
- (void)fixUniversalLink{
    
    UIApplication *application = [UIApplication sharedApplication];
    NSArray* arrChilden;
    //是否是iPhoneX
    if ([[application valueForKeyPath:@"statusBar"] isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
        arrChilden = [[[[application valueForKeyPath:@"statusBar"] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    } else {
        arrChilden = [[[application valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    }

    for (id child in arrChilden) {
        
        if ([child isKindOfClass:NSClassFromString(@"UIStatusBarOpenInSafariItemView")]) {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                Class cls =  [child class];
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wundeclared-selector"
                //原方法
                Method  originalM = class_getInstanceMethod(cls, @selector(userDidActivateButton:));
#pragma clang diagnostic pop
                //替換方法
                Method exchangeM = class_getInstanceMethod([self class], @selector(myDidActivateButton:));
                method_exchangeImplementations(originalM, exchangeM);
            });
        }
    }
}
#pragma mark - 通用鏈接修復(fù)
- (void)myDidActivateButton:(UIButton*)btn{
    NSLog(@"測(cè)試....");
    [[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"http://www.baidu.com"]];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,829評(píng)論 25 708
  • 2008年 7月10日 星期四 晴 地上一片狼藉,有盤(pán)子的碎片、玻璃杯的碎片,還有各種各樣其它碎片。 翁小姚又抓起...
    音樂(lè)如水閱讀 241評(píng)論 0 1
  • 今天又是周一,我看完了我的小說(shuō),并把它歸還了還回答了一個(gè)問(wèn)題,我覺(jué)得自己沒(méi)有答好這個(gè)問(wèn)題,下次我一定好好回答...
    原味石閱讀 282評(píng)論 0 0
  • 下定義是一件很難的事情,所有人都有自己的標(biāo)準(zhǔn),像是對(duì)于我,期待的定義是什么呢?大概就是出胡同的口,路兩邊飛奔的樹(shù),...
    繁意閱讀 162評(píng)論 0 0
  • 當(dāng)我在辦公桌前翻看著一年工作報(bào)告的時(shí)候,聽(tīng)見(jiàn)幾句爭(zhēng)吵聲音越來(lái)越大: 簡(jiǎn),這個(gè)稿子難道不是你寫(xiě)的?給我的職務(wù)...
    龍女卷風(fēng)閱讀 772評(píng)論 2 1