iOS WKWebview首次加載LocalStorage 問(wèn)題解決經(jīng)歷

背景


近期活烙,公司項(xiàng)目需要對(duì)接第三方公司H5頁(yè)面徐裸,其中遇到一個(gè)WKWebview網(wǎng)頁(yè)緩存在每次啟動(dòng)APP都會(huì)無(wú)故消失的問(wèn)題。H5使用的是localStorage啸盏,這個(gè)應(yīng)該是H5標(biāo)準(zhǔn)配置重贺,蘋果這么大的公司沒(méi)理由會(huì)犯這種錯(cuò)誤吧?于是,一段調(diào)試之旅就此開(kāi)始气笙。

WKWebview的坑


WKWebview的坑很多早有耳聞次企,但是真正發(fā)生在自己身上這還是第一次。常見(jiàn)的是第一次加載不帶cookies潜圃,或者兩個(gè)webview之間cookies不共享缸棵。但是localStorage和cookies雖有相似之處,但都是緩存谭期,說(shuō)不定也有同樣的問(wèn)題堵第,所以開(kāi)始網(wǎng)上搜索是否有相似問(wèn)題。


截屏2021-11-30 上午9.47.24.png

沒(méi)想到還真有很多和我一樣類似的問(wèn)題隧出,因?yàn)閕OS沒(méi)有提供獲取localStorage數(shù)據(jù)的方法踏志,所以只能通過(guò)原生調(diào)用JS的方式獲取和存儲(chǔ)LocalStorage,于是就引出下面第一個(gè)經(jīng)驗(yàn)。

通過(guò)js獲取和存儲(chǔ)localStorage


首先先說(shuō)思路胀瞪,第一次加載網(wǎng)頁(yè)之前针余,通過(guò)js將本地的localStorage數(shù)據(jù)通過(guò)JS腳本加入到網(wǎng)頁(yè)localStorage中,然后每次H5更新localStorage數(shù)據(jù)插入完畢凄诞,更新一份數(shù)據(jù)到本地沙盒圆雁。這樣就能解決第一次不帶localStorage數(shù)據(jù)的問(wèn)題。下面是引用網(wǎng)上的代碼:

NSString * userContent = [NSString stringWithFormat:@"{\"token\": \"%@\", \"userId\": %@}", @"a1cd4a59-974f-44ab-b264-46400f26c849", @"89"];
// 設(shè)置localStorage
NSString *jsString = [NSString stringWithFormat:@"localStorage.setItem('userContent', '%@')", userContent];
// 移除localStorage
// NSString *jsString = @"localStorage.removeItem('userContent')";
// 獲取localStorage
// NSString *jsString = @"localStorage.getItem('userContent')";
[self.webView evaluateJavaScript:jsString completionHandler:nil];

因?yàn)檫@段代碼中的js代碼比較簡(jiǎn)單帆谍,固定了字段名稱摸柄,但是現(xiàn)實(shí)中h5頁(yè)面很可能增減字段,所以我對(duì)這段代碼做了優(yōu)化:

 NSString * jsStr = @"var count = localStorage.length; var arr = new Array();for(var i=0;i<count;i++){ var key = localStorage.key(i);arr[i]= key;} arr;";
    [webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable data , NSError * _Nullable error) {
        if (data && ![data isKindOfClass:[NSNull class]]) {
            HSLogWithModule(2028,@"***XL 測(cè)試成功獲取到localStorage所有數(shù)據(jù)%@",data);
            if ([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSMutableArray class]]) {
                NSArray * arr = [data copy];
                for (NSInteger i = 0; i < arr.count; i ++) {
                    NSString * key =StringFromObject([NSString stringWithFormat:@"%@",arr[i]]);
                    [webView evaluateJavaScript:[NSString stringWithFormat:@"localStorage.getItem('%@')",key] completionHandler:^(id  data, NSError * _Nullable error) {
                        if (data && ![data isKindOfClass:[NSNull class]]) {
                            HSLogWithModule(2028,@"***XL 獲取%@成功2 %@",key,data);
                            [self.localStorageDic setObject:[NSString stringWithFormat:@"%@",data]  forKey:key];
                            [self.plistHelper WritePlistFileToDisk:self.localStorageDic];
                        }

                    }];
                }
            }
        }
    }];

先通過(guò)js獲取到本地localStorage所有key既忆,然后再逐個(gè)獲取值存儲(chǔ)到本地plist文件中驱负。由于考慮到很多js是異步請(qǐng)求執(zhí)行,所以觸發(fā)時(shí)機(jī)放到didFinishNavigation 2秒延時(shí)后調(diào)用患雇。

下面再來(lái)看看插入localStorage代碼:

- (void)setupLocalStrorageWithConfig:(WKWebViewConfiguration*)configuration dic:(NSMutableDictionary *)dic{
    HSLogWithModule(2028,@"***XL 準(zhǔn)備插入localStorage");
   if (![HSXLManager shareManager].haveLoadStorage) {

     NSString *jsString = @"";

      NSArray * keys = [dic allKeys];
       for (NSInteger i = 0;i < keys.count; i ++){
           NSString * key = keys[i];
          NSString * value = [dic objectForKey:key];
           jsString = [NSString stringWithFormat:@"%@ %@;", jsString,[NSString stringWithFormat:@"localStorage.setItem('%@', '%@')",key, value]];
     }

      HSLogWithModule(2028,@"***XL 腳本%@",jsString);

      [configuration.userContentController addUserScript:[[WKUserScript alloc] initWithSource:jsString injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]];

     HSLogWithModule(2028,@"***XL 插入腳本完成");
     [HSXLManager shareManager].haveLoadStorage = YES;
  }
   else {
      HSLogWithModule(2028,@"***XL 本次啟動(dòng)插入過(guò)跃脊,取消插入");
  }

}

在WKWebview初始化時(shí)配置WKWebViewConfiguration 的userContentController 插入腳本時(shí)機(jī)為WKUserScriptInjectionTimeAtDocumentStart ,完美苛吱。

使用開(kāi)發(fā)者模式+MAC Safari瀏覽器調(diào)試APPweb頁(yè)(驚喜)


在驗(yàn)證上述過(guò)程我還有一個(gè)意外收獲酪术,原來(lái)iOSweb頁(yè)的調(diào)試最正規(guī)的調(diào)試方法是用開(kāi)發(fā)者模式+MAC Safari瀏覽器!4浯ⅰ绘雁!
如果你申請(qǐng)過(guò)開(kāi)發(fā)者賬號(hào),并且在手機(jī)用開(kāi)發(fā)者賬號(hào)登錄appleid援所,那么會(huì)有一欄開(kāi)發(fā)者欄庐舟,并且在 設(shè)置->Safari瀏覽器->高級(jí) 中有個(gè)網(wǎng)頁(yè)檢查器開(kāi)關(guān),打開(kāi)它住拭。


網(wǎng)頁(yè)檢查器.PNG

然后在MAC電腦中safari瀏覽器的偏好設(shè)置里面挪略,打開(kāi)下方的開(kāi)發(fā)欄历帚。


mac1.png

然后打開(kāi)手機(jī)要調(diào)試的網(wǎng)頁(yè),在Mac開(kāi)發(fā)欄中選中你的手機(jī)杠娱,就可以看到需要調(diào)試的web頁(yè)的所有信息M炖巍!摊求!


mac2.png

再也不用寫輔助JS獲取web信息了禽拔!

WKWebview適配localStorage(最終解)


通過(guò)上面的方法和工具驗(yàn)證,我們確實(shí)發(fā)現(xiàn)localStorage在APP啟動(dòng)會(huì)消失室叉,并且用js腳本方法成功注入了數(shù)據(jù)奏赘。但是有一個(gè)問(wèn)題,web頁(yè)每次調(diào)用didFinishNavigation都會(huì)獲取最新的LocalStorage數(shù)據(jù)太惠,并且是遍歷一篇磨淌,非常的蠢。為了追求完美凿渊,我還是有點(diǎn)不死心的搜索梁只,最終有了驚人的發(fā)現(xiàn)。

其實(shí)在WKWebViewConfiguration中有一個(gè)websiteDataStore屬性埃脏,查了文檔是專門用來(lái)存儲(chǔ)本地?cái)?shù)據(jù)的搪锣。比如cookies session localStorage,官方文檔如下:


官方文檔.png

里面明確說(shuō)明有兩個(gè)類型 defaultDataStore 是存儲(chǔ)到本地的,nonPersistentDataStore 是存儲(chǔ)到內(nèi)存的彩掐。webview不通對(duì)象之所以不會(huì)共享緩存构舟,是因?yàn)樵诔跏蓟臅r(shí)候的config沒(méi)有配置websiteDataStore,沒(méi)有指定他存儲(chǔ)的地方堵幽!所以為了讓webview共享緩存存儲(chǔ)空間狗超,做如下修改


如下修改.png

另外還有些網(wǎng)頁(yè)說(shuō)要修改WKProcessPool為單例,為了保險(xiǎn)起見(jiàn)朴下,也加上努咐。

參考建議.png

問(wèn)題定位


defaultDataStore就是我們需要的類型,nonPersistentDataStore是那種無(wú)痕瀏覽才使用到的殴胧。那么問(wèn)題來(lái)了渗稍,明明defaultDataStore是存儲(chǔ)到本地硬盤的,那為什么殺死APP會(huì)獲取不到localStorage呢团滥?一個(gè)可怕的念頭出現(xiàn)了竿屹,會(huì)不會(huì)是APP自己清除了。灸姊。拱燃。于是我開(kāi)始搜索websiteDataStore相關(guān)代碼,果然在APP啟動(dòng)的時(shí)候調(diào)用了這段代碼厨钻。扼雏。


問(wèn)題定位.png

可能是之前做某些網(wǎng)頁(yè)功能時(shí)從網(wǎng)上抄的代碼,不理解什么意思就使用了夯膀。這段代碼會(huì)把本地的所有緩存都清除了诗充,而正常的清除手段應(yīng)該是根據(jù)URL去刪除其作用域下的緩存。

//清除系統(tǒng)相關(guān)cookies
+ (void)delCookiesWithDomain:(NSString *)domain{
    if (domain.length <=0) {
      
      return;
   }
    
   WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
  [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
                 completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) {
     for (WKWebsiteDataRecord *record  in records)
      {
         NSLog(@"**[XL]**刪除Web緩存**%@**type:%@*",record.displayName,record.dataTypes);
         if ( [record.displayName containsString:domain]) //取消備注诱建,可以針對(duì)某域名清除蝴蜓,否則是全清
           {
                [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes   forDataRecords:@[record]
                                                     completionHandler:^{
                   NSLog(@"Cookies for %@ deleted successfully",record.displayName);
                }];
           }
       }
   }];
}

總結(jié)


至此一個(gè)WKWebview首次不加載loaclStorage的問(wèn)題才根本解決。理論上是一段bug代碼引發(fā)的俺猿,但是不清楚為什么網(wǎng)上有那么多的小伙伴和我有一樣的遭遇茎匠。。押袍。所以這里寫篇文章诵冒,希望能讓有相同情況的小伙伴少走點(diǎn)彎路。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谊惭,一起剝皮案震驚了整個(gè)濱河市汽馋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圈盔,老刑警劉巖豹芯,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異驱敲,居然都是意外死亡铁蹈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門众眨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)现斋,“玉大人,你說(shuō)我怎么就攤上這事屹堰《嵯剩” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵姚建,是天一觀的道長(zhǎng)矫俺。 經(jīng)常有香客問(wèn)我,道長(zhǎng)掸冤,這世上最難降的妖魔是什么厘托? 我笑而不...
    開(kāi)封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮稿湿,結(jié)果婚禮上铅匹,老公的妹妹穿的比我還像新娘。我一直安慰自己饺藤,他們只是感情好包斑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布流礁。 她就那樣靜靜地躺著,像睡著了一般罗丰。 火紅的嫁衣襯著肌膚如雪神帅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天萌抵,我揣著相機(jī)與錄音找御,去河邊找鬼。 笑死绍填,一個(gè)胖子當(dāng)著我的面吹牛霎桅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讨永,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼滔驶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卿闹?” 一聲冷哼從身側(cè)響起瓜浸,我...
    開(kāi)封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎比原,沒(méi)想到半個(gè)月后插佛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡量窘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年雇寇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚌铜。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锨侯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冬殃,到底是詐尸還是另有隱情囚痴,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布审葬,位于F島的核電站深滚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涣觉。R本人自食惡果不足惜痴荐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望官册。 院中可真熱鬧生兆,春花似錦、人聲如沸膝宁。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至合蔽,卻和暖如春击敌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辈末。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工愚争, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留映皆,地道東北人挤聘。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捅彻,于是被迫代替她去往敵國(guó)和親组去。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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