IOS網(wǎng)絡(luò):HTTPCookie

原創(chuàng):知識進(jìn)階型文章
無私奉獻(xiàn)虐秦,為國為民平酿,創(chuàng)作不易,請珍惜悦陋,之后會持續(xù)更新染服,不斷完善
個人比較喜歡做筆記和寫總結(jié),畢竟好記性不如爛筆頭哈哈叨恨,這些文章記錄了我的IOS成長歷程柳刮,希望能與大家一起進(jìn)步
溫馨提示:由于簡書不支持目錄跳轉(zhuǎn),大家可通過command + F 輸入目錄標(biāo)題后迅速尋找到你所需要的內(nèi)容

目錄

  • 一痒钝、設(shè)置Cookie
    • 1秉颗、什么是Cookie
    • 2、獲得UIWebView的Cookies
    • 3送矩、設(shè)置UIWebView的Cookies
    • 4蚕甥、獲取WKWebView的Cookies
  • 二、解決WKWebView中的Cookies問題
    • 1栋荸、解決首次加載Cookie帶不上問題
    • 2菇怀、解決跳轉(zhuǎn)新頁面時Cookie帶不過去問題
      • 問題三:解決后續(xù)Ajax請求(局部頁面更新請求)Cookie丟失問題
      • 問題四:如果 response 里有 set-cookie 還需要緩存這些 cookie
  • 拓展:Cookie 污染問題
  • 更新:iOS 11后雙向同步cookie簡便方式
  • Demo
  • 參考文獻(xiàn)

一凭舶、設(shè)置Cookie

百度首頁Cookie

1、什么是Cookie

Cookie是由服務(wù)器端生成爱沟,發(fā)送給User-Agent(一般是瀏覽器或者客戶端)帅霜,瀏覽器會將Cookiekey/value保存到某個目錄下的文本文件內(nèi),下次請求同一網(wǎng)站地址時就發(fā)送該Cookie給服務(wù)器呼伸。Cookie必然會通過HTTPRespone傳過來身冀,并且CookieRespone中的HTTP header中。

為什么需要Cookie括享?

HTTP是一種無狀態(tài)的協(xié)議搂根,客戶端與服務(wù)器建立連接并傳輸數(shù)據(jù),數(shù)據(jù)傳輸完成后铃辖,連接就會關(guān)閉剩愧。再次交互數(shù)據(jù)需要建立新的連接,因此娇斩,服務(wù)器無法從連接上跟蹤會話隙咸,也無法知道用戶上一次做了什么。這嚴(yán)重阻礙了基于Web應(yīng)用程序的交互成洗,也影響用戶的交互體驗(yàn)五督。如:在網(wǎng)絡(luò)有時候需要用戶登錄才進(jìn)一步操作,用戶輸入用戶名密碼登錄后瓶殃,瀏覽了幾個頁面充包,由于HTTP的無狀態(tài)性,服務(wù)器并不知道用戶有沒有登錄遥椿。

Cookie是解決HTTP無狀態(tài)性的有效手段基矮,服務(wù)器可以設(shè)置或讀取Cookie中所包含的信息。當(dāng)用戶登錄后冠场,服務(wù)器會發(fā)送包含登錄憑據(jù)的Cookie到用戶瀏覽器客戶端家浇,而瀏覽器對該Cookie進(jìn)行某種形式的存儲(內(nèi)存或硬盤)。用戶再次訪問該網(wǎng)站時碴裙,瀏覽器會發(fā)送該CookieCookie未到期時)到服務(wù)器钢悲,服務(wù)器對該憑據(jù)進(jìn)行驗(yàn)證,合法時使用戶不必輸入用戶名和密碼就可以直接登錄舔株。

實(shí)際項(xiàng)目中使用場景如:當(dāng)Native端用戶是登錄狀態(tài)的莺琳,打開一個h5頁面,h5也要維持用戶的登錄狀態(tài)载慈。這個需求看似簡單惭等,如何實(shí)現(xiàn)呢?一般的解決方案是Native保存登錄狀態(tài)的Cookie办铡,在打開h5頁面中辞做,把Cookie添加上琳要,以此來維持登錄狀態(tài)。其實(shí)坑還是有很多的秤茅,比如用戶登錄或者退出了稚补,h5頁面的登錄狀態(tài)也變了,需要刷新嫂伞,什么時候刷新孔厉?WKWebView中Cookie丟失問題拯钻?

cookie的類型

Cookie總是由用戶客戶端進(jìn)行保存的(一般是瀏覽器)帖努,按其存儲位置可分為:內(nèi)存式Cookie(指在不設(shè)定它的生命周期expires時的狀態(tài))和硬盤式Cookie。內(nèi)存式Cookie存儲在內(nèi)存中粪般,瀏覽器關(guān)閉后就會消失拼余,由于其存儲時間較短,因此也被稱為非持久Cookie或會話Cookie亩歹。硬盤式Cookie保存在硬盤中匙监,其不會隨瀏覽器的關(guān)閉而消失,除非用戶手工清理或到了過期時間小作。由于硬盤式Cookie存儲時間是長期的亭姥,因此也被稱為持久Cookie

cookie實(shí)現(xiàn)原理

cookie定義了一些HTTP請求頭和HTTP響應(yīng)頭顾稀,通過這些HTTP頭信息使服務(wù)器可以與客戶端進(jìn)行狀態(tài)交互达罗。客戶端請求服務(wù)器后静秆,如果服務(wù)器需要記錄用戶狀態(tài)粮揉,服務(wù)器會在響應(yīng)信息中包含一個Set-Cookie的響應(yīng)頭,客戶端會根據(jù)這個響應(yīng)頭存儲Cookie信息抚笔。再次請求服務(wù)器時扶认,客戶端會在請求信息中包含一個Cookie請求頭,而服務(wù)器會根據(jù)這個請求頭進(jìn)行用戶身份殊橙、狀態(tài)等較驗(yàn)辐宾。

與session的區(qū)別

cookie機(jī)制采用的是在客戶端保持狀態(tài)的方案,而session機(jī)制采用的是在服務(wù)器端保持狀態(tài)的方案膨蛮。由于采用服務(wù)器端保持狀態(tài)的方案在客戶端也需要保存一個標(biāo)識螃概,所以session機(jī)制也需要借助于cookie機(jī)制來達(dá)到保存標(biāo)識的目的。

iOS中的Cookie

當(dāng)你訪問一個網(wǎng)站時鸽疾,NSURLRequest都會幫你主動記錄下來你訪問的站點(diǎn)設(shè)置的Cookie吊洼,如果Cookie 存在的話,會把這些信息放在 NSHTTPCookieStorage容器中共享制肮,當(dāng)你下次再訪問這個站點(diǎn)時冒窍,NSURLRequest會拿著上次保存下來了的Cookie繼續(xù)去請求递沪。

所以UIWebViewCookie管理很簡單,一般不需要我們手動操作Cookie综液,全部Cookie都會被[NSHTTPCookieStorage sharedHTTPCookieStorage]這個單例管理款慨,而且UIWebView會自動同步CookieStorage中的Cookie,所以只要我們在Native端谬莹,正常登陸退出檩奠,h5在適當(dāng)時候刷新,就可以正確的維持登錄狀態(tài)附帽,不需要做多余的操作埠戳。


1、獲得UIWebView的Cookies

實(shí)現(xiàn)webViewCookiesButton的調(diào)用方法webViewCookies:

- (void)webViewCookies
{
    // 創(chuàng)建新的UIWebView
    self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 400, self.view.bounds.size.width, 600)];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    [self.webView loadRequest:request];
    [self.view addSubview:self.webView];
    
    // 打印出所有cookie信息
    NSHTTPCookieStorage *storages = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [storages cookies])
    {
        NSLog(@"%@",cookie);
    }
}

又到了知識小課堂的時間蕉扮。NSHTTPCookieNSHTTPCookie對象代表一個HTTP cookie整胃。

//下面兩個方法用于對象的創(chuàng)建和初始化 都是通過字典進(jìn)行鍵值設(shè)置
- (nullable instancetype)initWithProperties:(NSDictionary<NSString *, id> *)properties;
+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary<NSString *, id> *)properties;

//返回Cookie數(shù)據(jù)中可用于添加HTTP頭字段的字典
+ (NSDictionary<NSString *, NSString *> *)requestHeaderFieldsWithCookies:(NSArray<NSHTTPCookie *> *)cookies;

//從指定的響應(yīng)頭和URL地址中解析出Cookie數(shù)據(jù)
+ (NSArray<NSHTTPCookie *> *)cookiesWithResponseHeaderFields:(NSDictionary<NSString *, NSString *> *)headerFields forURL:(NSURL *)URL;
//Cookie數(shù)據(jù)中的屬性字典
@property (nullable, readonly, copy) NSDictionary<NSString *, id> *properties;
//請求響應(yīng)的版本
@property (readonly) NSUInteger version;
//請求相應(yīng)的名稱
@property (readonly, copy) NSString *name;
//請求相應(yīng)的值
@property (readonly, copy) NSString *value;
//過期時間
@property (nullable, readonly, copy) NSDate *expiresDate;
//請求的域名
@property (readonly, copy) NSString *domain;
//請求的路徑
@property (readonly, copy) NSString *path;
//是否是安全傳輸
@property (readonly, getter=isSecure) BOOL secure;
//是否只發(fā)送HTTP的服務(wù)
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;
//響應(yīng)的文檔
@property (nullable, readonly, copy) NSString *comment;
//相應(yīng)的文檔URL
@property (nullable, readonly, copy) NSURL *commentURL;
//服務(wù)端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;

NSHTTPCookieStorage類采用單例的設(shè)計(jì)模式,其中管理著所有HTTP請求的Cookie信息喳钟,更改cookie的接收政策將會影響當(dāng)前所有正在使用cookieapp屁使。

//所有Cookie數(shù)據(jù)數(shù)組 其中存放NSHTTPCookie對象
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;   
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;//Cookie數(shù)據(jù)的接收協(xié)議
//獲取單例對象
+ (NSHTTPCookieStorage *)sharedHTTPCookieStorage;    
//手動設(shè)置一條Cookie數(shù)據(jù)
- (void)setCookie:(NSHTTPCookie *)cookie;   
//刪除某條Cookie信息
- (void)deleteCookie:(NSHTTPCookie *)cookie;    
//獲取某個特定URL的所有Cookie數(shù)據(jù)
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;    
//刪除某個時間后的所有Cookie信息 iOS8后可用
- (void)removeCookiesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);    
//為某個特定的URL設(shè)置Cookie
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL
// 存放和獲取一個task任務(wù)所對應(yīng)的cookie
- (void)storeCookies:(NSArray<NSHTTPCookie *> *)cookies forTask:(NSURLSessionTask *)task NS_AVAILABLE(10_10, 8_0);
- (void)getCookiesForTask:(NSURLSessionTask *)task completionHandler:(void (^) (NSArray<NSHTTPCookie *> * _Nullable cookies))completionHandler NS_AVAILABLE(10_10, 8_0);

系統(tǒng)下面的兩個通知與Cookie管理有關(guān):

//Cookie數(shù)據(jù)的接收協(xié)議改變時發(fā)送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerAcceptPolicyChangedNotification;
//管理的Cookie數(shù)據(jù)發(fā)生變化時發(fā)送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerCookiesChangedNotification;

看看運(yùn)行的結(jié)果打印出來的Cookie是怎樣的...

點(diǎn)擊webViewCookiesButton

Cookie結(jié)構(gòu).png

需要注意的是Cookie在在iOS中不會多應(yīng)用共享,但是會在不同進(jìn)程之間保持同步奔则,Session Cookie(一個isSessionOnly方法返回YESCookie)只能在單一進(jìn)程中使用蛮寂。至于其他屬性,在之前介紹NSHTTPCookie有提到易茬。


3酬蹋、設(shè)置UIWebView的Cookies

首先我們需要實(shí)現(xiàn)一個設(shè)置新Cookies的方法來對Cookies的各項(xiàng)屬性值進(jìn)行設(shè)置。

- (void)setCookieWithDomain:(NSString*)domainValue
                sessionName:(NSString *)name
               sessionValue:(NSString *)value
                expiresDate:(NSDate *)date

其中對各項(xiàng)屬性值進(jìn)行設(shè)置的部分如下:

// 創(chuàng)建字典存儲cookie的屬性值
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
// 設(shè)置cookie名
[cookieProperties setObject:name forKey:NSHTTPCookieName];
// 設(shè)置cookie值
[cookieProperties setObject:value forKey:NSHTTPCookieValue];
// 設(shè)置cookie域名
NSURL *url = [NSURL URLWithString:domainValue];
NSString *domain = [url host];
[cookieProperties setObject:domain forKey:NSHTTPCookieDomain];
// 設(shè)置cookie路徑 一般寫"/"
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
// 設(shè)置cookie版本, 默認(rèn)寫0
[cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];

設(shè)置cookie過期時間:

if (date)
{
    [cookieProperties setObject:date forKey:NSHTTPCookieExpires];
}
else
{
    // 推遲一年
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:([[NSDate date] timeIntervalSince1970] + 365*24*3600)];
    [cookieProperties setObject:date forKey:NSHTTPCookieExpires];
}

因?yàn)槭謩釉O(shè)置的Cookie不會自動持久化到沙盒疾呻,所以需要我們自己來實(shí)現(xiàn)除嘹。設(shè)置cookie的屬性值到本地磁盤,因?yàn)槭謩釉O(shè)置的Cookie不會自動持久化到沙盒岸蜗。

[[NSUserDefaults standardUserDefaults] setObject:cookieProperties forKey:@"app_cookies"];

接著在添加新的cookie之前尉咕,我們還需要刪除掉原來的cookie

// 刪除原cookie, 如果存在的話
NSArray * arrayCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookice in arrayCookies)
{
    // 清除特定某個cookie可以加個判斷: if ([cookie.name isEqualToString:@"cookiename"])
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookice];
}

使用字典初始化新的cookie

NSHTTPCookie *newcookie = [NSHTTPCookie cookieWithProperties:cookieProperties];

最后使用cookie管理器存儲cookie

[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:newcookie];

補(bǔ)充一點(diǎn),如果我們想清除某一個url緩存璃岳,可以這樣來做:

[NSURLCache sharedURLCache] removeCachedResponseForRequest:[NSURLRequest requestWithURL:url];

取出剛設(shè)置的新cookie設(shè)置請求頭:

- (void)setWebViewCookies
{
    // 設(shè)置新Cookies
    [self setCookieWithDomain:@"http://www.baidu.com" sessionName:@"xiejiapei_token_UIWebView" sessionValue:@"55555555" expiresDate:nil];
    
    // 取出剛設(shè)置的新cookie
    NSArray *cookiesArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSDictionary *headerCookieDict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookiesArray];
    
    // 設(shè)置請求頭
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    request.allHTTPHeaderFields = headerCookieDict;
    [self.webView loadRequest:request];
}

運(yùn)行APP驗(yàn)證下我們的Demo效果年缎。創(chuàng)建了新cookie,設(shè)置了其屬性后存儲下來铃慷。

創(chuàng)建了新`cookie`

取出剛設(shè)置的新cookie单芜,將其設(shè)置為請求頭

取出剛設(shè)置的新cookie將其設(shè)置為了請求頭

實(shí)際運(yùn)行后,通過Charles捕獲網(wǎng)絡(luò)請求犁柜,在狀態(tài)碼為302的請求的Content中我們看到確實(shí)存儲了剛才自己設(shè)置的cookie洲鸠,并且在本地沙盒Preferences中,打開.plist文件,cookie也成功保存到了本地

image.png

點(diǎn)擊webViewCookiesButton后扒腕,相應(yīng)的控制臺也的確打印出了我們設(shè)置的cookie


4绢淀、獲取WKWebView的Cookies

接下來的過程可能有點(diǎn)繞,最初我也整懵了......大家要做好心理準(zhǔn)備瘾腰。不知道蘋果為什么給WKWebView設(shè)置了這么一個坑皆的?原諒我才疏學(xué)淺不懂原因,要不是看了大家的文章蹋盆,都不知道還有這種鬼問題费薄。

UIWebViewCookie是通過 NSHTTPCookieStorage統(tǒng)一管理,服務(wù)器返回時寫入栖雾,發(fā)起請求時讀取楞抡,WebNative 通過該對象能共享 Cookie

說起WKWebview代替UIWebview帶來的好處你可以舉出一堆堆的例子岩灭,但說到 WKWebview的問題拌倍,除了WKWebview視圖尺寸問題赂鲤,默認(rèn)跳轉(zhuǎn)被屏蔽噪径,需要手動交互之外,你繞不過的就是WKWebview cookieNSHTTPCookieStorage cookie不共享的問題数初。如何將 NSHTTPCookieStorage 同步給WKWebview找爱,大概要處理很多種情況:

  • 初次加載頁面時,同步 cookieWKWebview
  • 如果 response 里有 set-cookie 還需要緩存這些 cookie
  • 如果是新頁面跳轉(zhuǎn)泡孩,還需要處理 cookie 傳遞的問題
  • 處理 ajax 請求時车摄,需要的 cookie

那么我們不禁好奇為什么NSHTTPCookieStorageWKWebview 沒有同步呢?首先來看看WKWebview cookie是怎么存儲的?

session 級別的 cookie保存在 WKProcessPool里仑鸥,每個 WKWebview 都可以關(guān)聯(lián)一個 WKProcessPool的實(shí)例吮播,如果需要在整個 App 生命周期里訪問 h5 保留 h5 里的登錄狀態(tài),可以使用 WKProcessPool的單例來共享登錄狀態(tài)眼俊。解釋下意狠,WKProcessPool 是個沒有屬性和方法的對象,唯一的作用就是標(biāo)識是不是需要新的 session 級別的管理對象疮胖,一個實(shí)例代表一個對象环戈。

未過期的 cookie。有效期內(nèi)的 cookie 被持久化存儲在 NSLibraryDirectory 目錄下的 Cookies/文件夾澎灸。com.xiejiapei.NSURLProtocolDemo.binarycookiesNSHTTPCookieStorage 文件對象院塞。cookie.binarycookies則是WKWebview的實(shí)例化對象。這也是為什么WKWebviewNSHTTPCookieStorage 沒有同步的原因——因?yàn)楸槐4嬖诓煌奈募?dāng)中性昭。

未過期的cookie存儲位置

為了驗(yàn)證拦止,你可以打開這兩者文件進(jìn)行查看:當(dāng)然兩個文件都是 binary file,直接用文本瀏覽器打開是看不到糜颠,有一個python寫的腳本BinaryCookieReader可以讀出來汹族,我不怎么懂python艺玲,就不展開了。

明白了存儲方式鞠抑,讓我們來思考??下WKWebview Cookie究竟是如何工作的饭聚?

系統(tǒng)默認(rèn)方式

當(dāng) webview loadRequest 或者 302重定向 或者在 webview 加載完畢觸發(fā)了 ajax請求時,WKWebview所需的 Cookie 會去 Cookie.binarycookies 里讀取本域名下的 Cookie搁拙,加上WKProcessPool持有的Cookie 一起作為request 頭里的Cookie 數(shù)據(jù)秒梳。

這種方式的問題是NSHTTPCookieStorageCookie 根本沒有共享給 WKWebview,沒有涉及到session暫不考慮WKProcessPool箕速,因此導(dǎo)致request 頭里的Cookie 數(shù)據(jù)為空酪碘,即allHTTPHeaderFields為空,這就是萬惡之源啊啊啊啊??~讓我們實(shí)際驗(yàn)證下控制臺輸出結(jié)果盐茎。

引入#import <WebKit/WebKit.h>兴垦,聲明會實(shí)現(xiàn)<WKNavigationDelegate>委托,實(shí)現(xiàn)wkWebViewCookiesButton的調(diào)用方法wkWebViewCookies

- (void)wkWebViewCookies
{
    // 創(chuàng)建新的WKWebView
    self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 400, self.view.bounds.size.width, 600)];
    self.wkWebView.navigationDelegate = self;
    [self.view addSubview:self.wkWebView];

    // 將cookie放在請求頭里面
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    NSLog(@"request.allHTTPHeaderFields: %@",request.allHTTPHeaderFields);
    [self.wkWebView loadRequest:request];
}
allHTTPHeaderFields是空的

Charles沒有捕獲到Cookie信息
// 這是上面??那一串完整的Cookie信息字柠,可以看到?jīng)]有我們自己設(shè)置的那部分信息
BAIDUID=B01696B5316606EBC8EFEADAF0444881:FG=1; H_WISE_SIDS=148077_149391_148504_143879_149356_150073_147087_141744_148193_148867_148435_147279_148824_149531_147638_148754_147897_146574_148523_149175_127969_146548_149329_149719_146652_147024_146732_138426_149558_149617_131423_100805_147527_107314_147136_148570_148185_147717_149251_146395_144966_149279_145607_139884_148048_148752_148869_146046_110085; BD_BOXFO=_avOi_aivYo7C; SE_LAUNCH=5%3A26542282_3%3A26542286; bd_af=1; BDORZ=AE84CDB3A529C0F8A2B9DCDD1D18B695

需要注意的是探越,并非說系統(tǒng)的NSHTTPCookieStorageWKWebView中所有Cookie都無法自動同步,兩個存儲文件完全各自為政窑业。WKWebView加載網(wǎng)頁得到的Cookie會同步到NSHTTPCookieStorage中(優(yōu)秀??)钦幔。但是WKWebView加載請求時,不會同步NSHTTPCookieStorage中已有的Cookie(最為致命??)常柄。既然發(fā)現(xiàn)了問題鲤氢,接下來就要大刀闊斧地干了! (兇惡嘴臉??)


二、解決WKWebView中的Cookies問題

1西潘、解決首次加載Cookie帶不上問題

這個比較簡單卷玉,Cookies數(shù)組轉(zhuǎn)換為requestHeaderFields,再將其設(shè)置為請求頭即可喷市,這樣相种,只要你保證sharedHTTPCookieStorage中你的Cookie存在,首次訪問一個頁面东抹,就不會有問題蚂子。

- (void)wkWebViewCookies
{
    // 創(chuàng)建新的WKWebView
    self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 400, self.view.bounds.size.width, 600)];
    self.wkWebView.navigationDelegate = self;
    [self.view addSubview:self.wkWebView];

    // 將cookie放在請求頭里面
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    NSArray  *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    // Cookies數(shù)組轉(zhuǎn)換為requestHeaderFields
    NSDictionary *requestHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    // 設(shè)置請求頭
    request.allHTTPHeaderFields = requestHeaderFields;
    NSLog(@"request.allHTTPHeaderFields: %@",request.allHTTPHeaderFields);
    [self.wkWebView loadRequest:request];
}

看下運(yùn)行效果,發(fā)現(xiàn)我們成功將其設(shè)置為了請求頭缭黔,這樣request.allHTTPHeaderFields就不為空了,并且Charles也捕獲到了該Cookie信息馏谨。

設(shè)置Cookie作請求頭

Charles也捕獲到了該Cookie信息


2、解決跳轉(zhuǎn)新頁面時Cookie帶不過去問題

這里的問題是當(dāng)你點(diǎn)擊頁面上的某個鏈接喇伯,跳轉(zhuǎn)到新的頁面买喧,Cookie又丟了......好弱智啊......怎么解決呢淤毛?新建了一個WKCookieManager工具類今缚,用更安全的方式設(shè)置了一個單例來方便調(diào)用之后的方法。

+ (instancetype)shareManager
{
    // 靜態(tài)局部變量
    static WKCookieManager *_instance;
    // 通過dispatch_ once方式確保instance在多線程環(huán)境下只被創(chuàng)建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 創(chuàng)建實(shí)例
        // super: 不能使用self,否則重寫的allocWithZone第一次初始化的時候 會循環(huán)調(diào)用instance
        _instance = [[super allocWithZone:NULL] init];
    });
    return _instance;
}
// 重寫方法[必不可少]
// 規(guī)避逃脫sharedInstance再去創(chuàng)建其他對象低淡,當(dāng)alloc的時候只能返回單例
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    return [self shareManager];
}

.h文件里聲明了fixNewRequestCookieWithRequest方法

/**
 解決新的跳轉(zhuǎn) Cookie 丟失問題
 @param originalRequest 攔截的請求
 @return 帶上 Cookie 的新請求
 */
- (NSURLRequest *)fixNewRequestCookieWithRequest:(NSURLRequest *)originalRequest;

.m文件中來實(shí)現(xiàn)該方法姓言,首先需要注意的是如果navigationAction.requestNSURLRequest,不可變蔗蹋,那不就添加不了Cookie了何荚,但我們不能因?yàn)檫@個問題不允許跳轉(zhuǎn),所以我們這里需要讓它可變纸颜。其中因?yàn)閭魅胧?code>NSURLRequest兽泣,但是其實(shí)際類型為NSMutableURLRequest绎橘,我們就可以根據(jù)里氏替換原則對其進(jìn)行運(yùn)行時強(qiáng)制轉(zhuǎn)化為子類胁孙。而當(dāng)其為NSURLRequest,只需要進(jìn)行可變拷貝即可称鳞,為深拷貝涮较。里氏替換原則指的是父類可以被子類無縫替換,且原有功能不受影響冈止,例如KVO實(shí)現(xiàn)原理狂票,調(diào)用addObserver方法,系統(tǒng)在動態(tài)運(yùn)行時候?yàn)槲覀儎?chuàng)建一個子類熙暴,我們雖然感受到的是使用原有的父類闺属,實(shí)際上是子類。

NSMutableURLRequest *fixedRequest;
if ([originalRequest isKindOfClass:[NSMutableURLRequest class]])
{
    fixedRequest = (NSMutableURLRequest *)originalRequest;
}
else
{
    // 只需要進(jìn)行可變拷貝即可
    fixedRequest = originalRequest.mutableCopy;
}

取出解決問題一時候的NSHTTPCookieStorage中的Cookie周霉,并將其設(shè)置為fixedRequest.allHTTPHeaderFields掂器,其實(shí)解決思路都一樣,就是它沒有那么就從保存下來的地方給它一個就好了俱箱。

    // 關(guān)鍵步驟:防止Cookie丟失
    // 前提是保證sharedHTTPCookieStorage中你的Cookie存在
    NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
    if (dict.count)
    {
        NSMutableDictionary *mDict = originalRequest.allHTTPHeaderFields.mutableCopy;
        [mDict setValuesForKeysWithDictionary:dict];
        fixedRequest.allHTTPHeaderFields = mDict;
    }
    return fixedRequest;

打斷點(diǎn)調(diào)試下国瓮,看是否能行,結(jié)果顯示是OK的:

跳轉(zhuǎn)新頁面能拿到Cookie了

問題三:解決后續(xù)Ajax請求(局部頁面更新請求)Cookie丟失問題

AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)。
AJAX 不是新的編程語言乃摹,而是一種使用現(xiàn)有標(biāo)準(zhǔn)的新方法禁漓。
AJAX 最大的優(yōu)點(diǎn)是在不重新加載整個頁面的情況下,可以與服務(wù)器交換數(shù)據(jù)并更新部分網(wǎng)頁內(nèi)容孵睬。
AJAX 不需要任何瀏覽器插件播揪,但需要用戶允許JavaScript在瀏覽器上執(zhí)行。

解決此問題的關(guān)鍵是注入的 JS 代碼塊熏兄。
a姜贡、在.h文件里聲明了fixNewRequestCookieWithRequest方法

/**
 Ajax請求(局部頁面更新請求)Cookie 丟失問題
 @return 注入的 JS 代碼塊
 */
- (WKUserScript *)futhureCookieScript;

b、在.m文件中來實(shí)現(xiàn)該方法磷支,此處需要注意forMainFrameOnly為NO谒撼,因?yàn)槲覀冃枰獙ookie注入到所有frames

// Ajax請求(局部頁面更新請求)Cookie 丟失問題
- (WKUserScript *)futhureCookieScript
{
    // 只讀屬性,表示JS是否應(yīng)該注入到所有的frames中還是只有main frame
    WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:[self cookieString] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    return cookieScript;
}

相應(yīng)JS腳本如下:

- (NSString *)cookieString
{
    NSMutableString *script = [NSMutableString string];
    [script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
    for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {

        if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
            continue;
        }
        [script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.xjp_formatCookieString];
    }
    return script;
}

此處需要寫個將cookie格式化為string的擴(kuò)展方法:

#import "NSHTTPCookie+Util.h"

@implementation NSHTTPCookie (Util)

// 將cookie格式化為string的擴(kuò)展方法
- (NSString *)xjp_formatCookieString{
    NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
                        self.name,
                        self.value,
                        self.domain,
                        self.path ?: @"/"];
    
    if (self.secure) {
        string = [string stringByAppendingString:@";secure=true"];
    }
    
    return string;
}

@end

c雾狈、接著在HTTPCookieViewController中調(diào)用我們剛才實(shí)現(xiàn)的方法廓潜,此時創(chuàng)建新的WKWebView需要采用configuration的初始化方式,為了向contoller中注入腳本

    // 創(chuàng)建新的WKWebView善榛,該用configuration的初始化方式辩蛋,為了向contoller中注入腳本
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    WKUserContentController *contoller = [[WKUserContentController alloc] init];
    [contoller addUserScript:[[WKCookieManager shareManager] futhureCookieScript]];
    configuration.userContentController = contoller;
    self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 400, self.view.bounds.size.width, 600) configuration:configuration];
    self.wkWebView.navigationDelegate = self;
    [self.view addSubview:self.wkWebView];

大功告成,同樣只要你保證sharedHTTPCookieStorage中你的Cookie存在移盆,后續(xù)Ajax請求就不會有問題悼院。

問題四:如果 response 里有 set-cookie 還需要緩存這些 cookie
保證sharedHTTPCookieStorage中你的Cookie存在。怎么保證呢咒循?由于WKWebView加載網(wǎng)頁得到的Cookie會同步到NSHTTPCookieStorage中的特點(diǎn)据途,有時候你強(qiáng)行添加的Cookie會在同步過程中丟失。Charles抓包發(fā)現(xiàn)點(diǎn)擊一個鏈接時叙甸,Requestheader中多了Set-Cookie字段颖医,其實(shí)Cookie已經(jīng)丟了。

解決方案那就是把自己需要的Cookie主動保存起來裆蒸,每次調(diào)用[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies方法時熔萧,保證返回的數(shù)組中有自己需要的Cookie。下面上代碼僚祷,用了runtimeMethod Swizzling佛致。

a、創(chuàng)建NSHTTPCookieStorage (CookieUtil)擴(kuò)展方法文件辙谜,引入運(yùn)行時#import <objc/runtime.h>框架俺榆,接著實(shí)現(xiàn)class_methodSwizzling替換方法:

/**
*  方法替換。Method Swizzling技術(shù)筷弦。使類中的方法實(shí)現(xiàn)和自己的方法實(shí)現(xiàn)互換肋演,達(dá)到替換默認(rèn)抑诸,且還可以調(diào)用默認(rèn)方法的目的。
*
*  @param class            替換的方法所屬的類
*  @param originalSelector 原始的方法選擇器
*  @param swizzledSelector 用以替換的方法選擇器
*/
static inline void class_methodSwizzling(Class class, SEL originalSelector, SEL swizzledSelector)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    // 如果可以在原有類中添加方法爹殊,說明原有的類并沒有實(shí)現(xiàn)蜕乡,可能是繼承自父類的方法。
    // 那么层玲,我們添加一個方法反症,方法名為原方法名,實(shí)現(xiàn)為我們自己的實(shí)現(xiàn)润绵。之后再將自己的方法替換成原始的實(shí)現(xiàn)。
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    
    //這么做胞谈,避免了替換方法時尘盼,由于本class中沒有實(shí)現(xiàn),從而替換了父類的方法烦绳。造成不可預(yù)知的錯誤卿捎。
    if (didAddMethod)
    {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    // 如果類中已經(jīng)實(shí)現(xiàn)了這個原始方法,那么就與我們的方法互換一下實(shí)現(xiàn)即可径密。
    else
    {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

b午阵、接著需要在load方法中調(diào)用我們的替換方法,將cookies的GET方法替換為我們自定義的custom_cookiesGet方法:

// 加載
+ (void)load
{
    class_methodSwizzling(self, @selector(cookies), @selector(custom_cookies));
}

c享扔、于是我們需要實(shí)現(xiàn)一下這個自定義的Get方法custom_cookies

// 自定義cookies
- (NSArray<NSHTTPCookie *> *)custom_cookies
{
    // 獲取到之前的所有cookies
    NSArray *cookies = [self custom_cookies];
    BOOL isExist = NO;
    
    // 尋找Custom_Client_Cookie
    for (NSHTTPCookie *cookie in cookies)
    {
        if ([cookie.name isEqualToString:@"Custom_Client_Cookie"])
        {
            isExist = YES;
            break;
        }
    }
    
    // 尋找不到則向CookieStroage中添加
    if (!isExist)
    {
        // 添加到NSHTTPCookieStorage底桂,其中fetchAccessTokenCookie為創(chuàng)建新Cookie的方法
        NSHTTPCookie *cookie = [self fetchAccessTokenCookie];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        
        // 添加到返回?cái)?shù)組中
        NSMutableArray *mutableCookies = cookies.mutableCopy;
        [mutableCookies addObject:cookie];
        cookies = mutableCookies.copy;
    }
    
    return cookies;
}

d、如果NSHTTPCookieStorage沒有我們想要的Cookie伪很,就需要我們創(chuàng)建一個戚啥,創(chuàng)建新CookiefetchAccessTokenCookie方法如下:

// 創(chuàng)建新Cookie
- (NSHTTPCookie *)fetchAccessTokenCookie
{
    NSMutableDictionary *properties = [NSMutableDictionary dictionary];
    [properties setObject:@"Custom_Client_Cookie" forKey:NSHTTPCookieName];
    [properties setObject:@"Cooci" forKey:NSHTTPCookieValue];
    [properties setObject:@"" forKey:NSHTTPCookieDomain];
    [properties setObject:@"/" forKey:NSHTTPCookiePath];
    NSHTTPCookie *accessCookie = [[NSHTTPCookie alloc] initWithProperties:properties];
    return accessCookie;
}

e、接下來需要在合適的時候(如登錄成功)保存Cookie锉试,實(shí)現(xiàn)該方法后,在viewDidLoad中調(diào)用

// 在合適的時候(如登錄成功)保存Cookie
- (void)saveCookie
{
    NSArray *allCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    for (NSHTTPCookie *cookie in allCookies)
    {
        // 找到Custom_Client_Cookie
        if ([cookie.name isEqualToString:@"Custom_Client_Cookie"])
        {
            NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"Custom_Client_Cookie"];
            if (dict)
            {
                // 本地Cookie有更新
                NSHTTPCookie *localCookie = [NSHTTPCookie cookieWithProperties:dict];
                if (![cookie.value isEqual:localCookie.value])
                {
                    NSLog(@"本地Cookie有更新");
                }
            }
            
            // 更新保存
            [[NSUserDefaults standardUserDefaults] setObject:cookie.properties forKey:@"Custom_Client_Cookie"];
            [[NSUserDefaults standardUserDefaults] synchronize];
            break;
        }
    }
}

看看運(yùn)行結(jié)果如何览濒?
運(yùn)行后首先會進(jìn)入方法交換方法class_methodSwizzling

class_methodSwizzling

進(jìn)入HTTPCookieViewController頁面后馬上會進(jìn)入saveCookie方法呆盖,由于NSArray *allCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];調(diào)用了cookies的Get方法,所以又立刻進(jìn)入到custom_cookies中贷笛,第一次因?yàn)椴淮嬖谧远xCookies需要進(jìn)行創(chuàng)造并存儲应又,所以mutableCookies擁有兩個與元素,而cookie卻擁有一個乏苦。

custom_cookies

最后又重新進(jìn)入到saveCookie方法,將以前保存的本地Cookie和我們剛剛新設(shè)置的custom_cookies的值進(jìn)行比較盆繁,我第一次設(shè)置的是linning,第二次設(shè)置為xiejiapei冕碟,因?yàn)閮纱尾幌嗟劝菜拢暂敵?code>cookies的值更新了我衬。

saveCookie

拓展:Cookie 污染問題

原因:如果我們自己設(shè)置了 allHTTPHeaderFields,則系統(tǒng)不會使用 the cookie manager by default破加。
解決方案:所以我們的方案是在頁面加載過程中不去設(shè)置 allHTTPHeaderFields,全部使用默認(rèn) Cookie mananger管理锭环,這樣就不會有 Cookie 污染也不會有302 Cookie丟失的問題了辅辩。
唯一的問題:如何將 NSHTTPCookieStorageCookie共享給WKWebview
`

實(shí)踐過程如下

在首次加載 url時撩鹿,檢查是否已經(jīng)同步過 Cookie节沦。如果沒有同步過吼鳞,則先加載 一個 cookieWebivew赖条,它的主要目的就是將 Cookie先使用 usercontroller 的方式寫到WKWebview里纬乍,這樣在處理正式的請求時仿贬,就會帶上我們從NSHTTPCookieStorage 獲取到的 Cookie了。核心代碼如下:

if ([AppHostCookie loginCookieHasBeenSynced] == NO) {
        //
        NSURL *cookieURL = [NSURL URLWithString:kFakeCookieWebPageURLString];
        NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:cookieURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:120];
        WKWebView *cookieWebview = [self getCookieWebview];
        [self.view addSubview:cookieWebview];
        [cookieWebview loadRequest:mutableRequest];
        DDLogInfo(@"[JSBridge] preload cookie for url = %@", self.loadUrl);
    } else {
        [self loadWebPage];
    }
//  注意队伟,CookieWebview 和 正常的 webview 是不同的
- (WKWebView *)getCookieWebview
{
    // 設(shè)置加載頁面完畢后,里面的后續(xù)請求锈颗,如 xhr 請求使用的cookie
    WKUserContentController *userContentController = [WKUserContentController new];

    WKWebViewConfiguration *webViewConfig = [[WKWebViewConfiguration alloc] init];
    webViewConfig.userContentController = userContentController;

    webViewConfig.processPool = [AppHostCookie sharedPoolManager];
    
    NSMutableArray<NSString *> *oldCookies = [AppHostCookie cookieJavaScriptArray];
    [oldCookies enumerateObjectsUsingBlock:^(NSString *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
        NSString *setCookie = [NSString stringWithFormat:@"document.cookie='%@';", obj];
        WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:setCookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
        [userContentController addUserScript:cookieScript];
    }];

    WKWebView *webview = [[WKWebView alloc] initWithFrame:CGRectMake(0, -1, SCREEN_WIDTH,ONE_PIXEL) configuration:webViewConfig];

    webview.navigationDelegate = self;
    webview.UIDelegate = self;

    return webview;
}

這里需要處理的問題是,加載完畢或者失敗后需要清理舊 webview和設(shè)置標(biāo)記位覆醇。

static NSString * _Nonnull kFakeCookieWebPageURLString = @"http://ai.api.com/xhr/user/getUid.do?26u-KQa-fKQ-3BD"
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{

    NSURL *targetURL = webView.URL;
    if ([AppHostCookie loginCookieHasBeenSynced] == NO && targetURL.query.length > 0 && [kFakeCookieWebPageURLString containsString:targetURL.query]) {
        [AppHostCookie setLoginCookieHasBeenSynced:YES];
        // 加載真正的頁面;此時已經(jīng)有 App 的 cookie 存在了。
        [webView removeFromSuperview];
        [self loadWebPage];
        return;
    }
}

同時記得刪掉原來對 webviewCookie 的所有處理的代碼凿试。
處理至此,大功告成板甘,這樣的后續(xù)請求, WKWebview都用自身所有的CookieNSHTTPCookieStorageCookie在跳,這樣既達(dá)到了 Cookie 共享的目的猫妙,WKWebviewNSHTTPCookieStorageCookie也做了個隔離割坠。

這個方法彼哼,我看得懵懵懂懂,大家想要深入研究的話象浑,在這個開源項(xiàng)目 https://github.com/hite/AppHostExample/ 里有使用舉例篓吁,具體的代碼寫在 https://github.com/hite/AppHost 這個庫里杖剪。

更新:iOS 11后雙向同步cookie簡便方式

沒親自嘗試過盛嘿,先貼在這兒,以后試下锹锰,寫下流程。
.h文件:

//
//  UWWkWebViewCookieManager.h
//
//  Created by DarkAngel on 2018/4/12.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
/**
 WKWebView的Cookie管理,只用于iOS 11以上
 */
@interface UWWkWebViewCookieManager : NSObject
/**
 從NSHTTPCookieStorage同步cookie
 */
+ (void)synchronizeCookiesFromNSHTTPCookieStorage NS_AVAILABLE_IOS(11_0);

@end

NS_ASSUME_NONNULL_END

.m文件:

//
//  UWWkWebViewCookieManager.m
//
//  Created by DarkAngel on 2018/4/12.
//

#import "UWWkWebViewCookieManager.h"
#import <WebKit/WebKit.h>
#import "GCDMethods.h"

@interface UWWkWebViewCookieManager () <WKHTTPCookieStoreObserver>

@end

@implementation UWWkWebViewCookieManager

+ (void)load
{
    if (@available(iOS 11.0, *)) {
        [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] addObserver:(id<WKHTTPCookieStoreObserver>)self];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cookiesDidChangeInHTTPCookieStorage:) name:NSHTTPCookieManagerCookiesChangedNotification object:nil];
    }
}

/**
 從[NSHTTPCookieStorage sharedHTTPCookieStorage]同步Cookie到WKHTTPCookieStore
 */
+ (void)synchronizeCookiesFromNSHTTPCookieStorage NS_AVAILABLE_IOS(11_0)
{
    if (@available(iOS 11.0, *)) {
        GCD_MAIN_SYNC(^{
            [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull wkCookies) {
                NSMutableSet *before = [NSMutableSet setWithArray:wkCookies];
                NSSet *after = [NSSet setWithArray:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
                //需要保留的
                NSMutableSet *toKeep = [NSMutableSet setWithSet:before];
                [toKeep intersectSet:after];
                //需要添加的
                NSMutableSet *toAdd = [NSMutableSet setWithSet:after];
                [toAdd minusSet:toKeep];
                //需要刪除的
                NSMutableSet *toRemove = [NSMutableSet setWithSet:before];
                [toRemove minusSet:after];
                for (NSHTTPCookie *cookie in toRemove.allObjects) {
                    [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] deleteCookie:cookie completionHandler:nil];
                }
                for (NSHTTPCookie *cookie in toAdd.allObjects) {
                    [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] setCookie:cookie completionHandler:nil];
                }
            }];
        });
    } else {
        
    }
}

/**
 從WKHTTPCookieStore同步Cookie到[NSHTTPCookieStorage sharedHTTPCookieStorage]
 */
+ (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore NS_AVAILABLE_IOS(11_0)
{
    GCD_MAIN(^{
        [cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            NSSet *before = [NSSet setWithArray:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
            NSMutableSet *after = [NSMutableSet setWithArray:cookies];
            //需要保留的
            NSMutableSet *toKeep = [NSMutableSet setWithSet:before];
            [toKeep intersectSet:after];
            //需要添加的
            NSMutableSet *toAdd = [NSMutableSet setWithSet:after];
            [toAdd minusSet:toKeep];
            for (NSHTTPCookie *cookie in toAdd.allObjects) {
                [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
            }
        }];
    });
}
/**
 從[NSHTTPCookieStorage sharedHTTPCookieStorage]同步Cookie到WKHTTPCookieStore
 */
+ (void)cookiesDidChangeInHTTPCookieStorage:(NSNotification *)notification
{
    if (@available(iOS 11.0, *)) {
        [self synchronizeCookiesFromNSHTTPCookieStorage];
    }
}

@end

Demo

Demo在我的Github上,歡迎下載少态。
IOSAdvancedDemo

推薦Demo
iOS中UIWebView與WKWebView、JavaScript與OC交互澳骤、Cookie管理看我就夠

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載棋枕,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者重斑。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市漾脂,隨后出現(xiàn)的幾起案子骨稿,更是在濱河造成了極大的恐慌镜豹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件例衍,死亡現(xiàn)場離奇詭異佛玄,居然都是意外死亡梦抢,警方通過查閱死者的電腦和手機(jī)奥吩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來端衰,“玉大人,你說我怎么就攤上這事抵代≈魇兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵涎才,是天一觀的道長邑闺。 經(jīng)常有香客問我,道長伴挚,這世上最難降的妖魔是什么颅眶? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮商叹,結(jié)果婚禮上沈自,老公的妹妹穿的比我還像新娘。我一直安慰自己酪夷,他們只是感情好晚岭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著片择,像睡著了一般啰挪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锰什,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天果港,我揣著相機(jī)與錄音辛掠,去河邊找鬼。 笑死没咙,一個胖子當(dāng)著我的面吹牛猩谊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祭刚,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼牌捷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涡驮?” 一聲冷哼從身側(cè)響起暗甥,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡保檐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粘茄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搂鲫。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锯岖,靈堂內(nèi)的尸體忽然破棺而出捶牢,到底是詐尸還是另有隱情灸蟆,我是刑警寧澤斋枢,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布朝扼,位于F島的核電站搂捧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渊额。R本人自食惡果不足惜矗晃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一地沮、第九天 我趴在偏房一處隱蔽的房頂上張望辞居。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婿滓。三九已至,卻和暖如春纳猫,著一層夾襖步出監(jiān)牢的瞬間诗舰,已是汗流浹背渔呵。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工爷辱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留双饥,地道東北人锉桑。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓款筑,卻偏偏與公主長得像腾么,于是被迫代替她去往敵國和親奈梳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345