すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud DNS:iOS WebView で Alibaba Cloud HTTPDNS SDK を統合するためのベストプラクティス

最終更新日:Nov 09, 2025

このトピックでは、iOS クライアントの WebView に Alibaba Cloud HTTPDNS SDK を統合するためのベストプラクティスについて説明します。

概要

アプリの WebView がネットワークリクエストを読み込むとき、iOS システムはシステム API を使用してこれらのリクエストをインターセプトし、カスタムロジックの挿入を実装できます。ただし、WebView でネットワークリクエストをインターセプトした後、iOS システムは IP アドレスに基づいてネットワークリクエストの送信、データの受信、ページのリダイレクト、ページのデコード、Cookie、キャッシュ、およびその他の機能を処理する必要があります。全体的に見て、WebView で IP アドレスベースの直接接続に SDK 解決を使用するには、実装のしきい値が高くなります。モバイル オペレーティングシステムは、このシナリオに対するサポートが限られており、いくつかの欠陥があります。開発者は、これらの問題を回避および最適化するために、ネットワーク/OS フレームワークに対する強力なコードレベルの制御を行う必要があります。

警告

Apple は UIWebView を非推奨にしており、WKWebView には成熟した IP アドレスベースの直接接続ソリューションがありません。登録とインターセプトにはプライベート API しか使用できません。プライベート API を使用すると、Apple によって拒否されるリスクがあります。開発者は注意して使用する必要があります。

NSURLProtocol を使用した UIWebView でのリクエストのインターセプト

  • NSURLProtocol に基づいて、iOS システム上の上位層ネットワークライブラリ NSURLConnection/NSURLSession によって送信されたネットワークリクエストをインターセプトできます。UIWebView によって送信されたリクエストも含まれます。

  • インターフェイスを介してカスタム NSURLProtocol を登録して、UIWebView 上位層ネットワークリクエストをインターセプトし、データの送受信、リダイレクト、およびその他の処理ロジックを処理する新しいネットワークリクエストを作成し、結果を元の リクエストに戻します。

    [NSURLProtocol registerClass:[CustomProtocol class]]; // カスタムプロトコルの登録
  • カスタム NSURLProtocol 処理の概要:

    • canInitWithRequest でドメイン名解決が必要なリクエストをインターセプトします。

    • リクエストがインターセプトされた後、Alibaba Cloud HTTPDNS を使用して名前解決を実行します。

    • 解決が完了したら、通常の リクエストのように URL.host フィールドと HTTP ヘッダーホストドメインを置き換え、データの送受信、リダイレクト、およびその他の リクエスト処理を処理します。

    • NSURLProtocol インターフェイスを介して リクエスト処理結果を元の UIWebView リクエストに戻します。

  • NSURLProtocol の使用方法については、「Apple NSURLProtocol API」を参照してください。Apple の公式サンプルコードについては、「Apple サンプルコード - CustomHTTPProtocol」を参照してください。

プライベート API を使用した WKWebView でのリクエストの登録とインターセプト

WKWebView は、アプリプロセスとは別のプロセスでネットワークリクエストを実行します。リクエストデータはメインプロセスを通過しません。したがって、WKWebView で NSURLProtocol を直接使用しても、リクエストをインターセプトできません。

現在、WKWebView シナリオに対応する成熟した IP アドレスベースの直接接続ソリューションはありません。以下では、プライベート API を使用して登録とインターセプトを行う方法について説明します。

  // 独自のプロトコルを登録する
  [NSURLProtocol registerClass:[CustomProtocol class]];
  // WKWebview を作成する
  WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init];
  WKWebView * wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0,   [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:config];
  [wkWebView loadRequest:webViewReq];
  [self.view addSubview:wkWebView];
  // スキームを登録する
  Class cls = NSClassFromString(@"WKBrowsingContextController");
  SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
  if ([cls respondsToSelector:sel]) {
      // http および https リクエストを介して、同様に他のスキームを介しても同様ですが、ULR ローディングシステムを満たす必要があります
      [cls performSelector:sel withObject:@"http"];
      [cls performSelector:sel withObject:@"https"];
  } // カスタムプロトコルの登録とスキームの登録

NSURLProtocol の使用方法については、「Apple NSURLProtocol API」を参照してください。Apple の公式サンプルコードについては、「Apple サンプルコード - CustomHTTPProtocol」を参照してください。

警告

実行が遅すぎるのを避けるため、+load メソッドで実行する必要があります。- (void)viewDidLoad で登録すると、登録が遅すぎるために問題が発生する可能性があります。

WKWebView での Cookie 処理のための IP アドレスベースの直接接続ソリューションの使用

後続の操作を実行する前に、次の問題に注意してください。

  • WKWebView は Cookie 管理に最適ではありません。iOS 11 の WKWebView では Cookie 管理が最適化されていますか? iOS 11 の WKWebView で Cookie 管理が最適化されている場合、ユーザーは最適化によってどのようなメリットがありますか?

  • サーバーへの接続に IP アドレスを使用する場合、サーバーから返される Cookie のドメインフィールドの値も IP アドレスになります。IP アドレスが動的である場合、次の問題が発生する可能性があります。一部の HTML5 アプリのログイン状態の検証は Cookie に依存しています。ただし、WKWebView のリクエストには Cookie が自動的に含まれません。

    WKWebView で NSURLProtocol を使用してリクエストをインターセプトしても、Cookie 情報を取得できません

    iOS 11 では、WKWebView から Cookie 情報をインターセプトするために使用できる新しい API WKHTTPCookieStore が導入されました。

    例:

    WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
       // Cookie を取得する
        [cookieStroe getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            NSLog(@"すべての Cookie %@",cookies);
        }];
    
        // Cookie を設定する
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSHTTPCookieName] = @"userid";
        dict[NSHTTPCookieValue] = @"123";
        dict[NSHTTPCookieDomain] = @"xxxx.com";
        dict[NSHTTPCookiePath] = @"/";
    
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:dict];
        [cookieStroe setCookie:cookie completionHandler:^{
            NSLog(@"Cookie を設定する");
        }];
    
        // Cookie を削除する
        [cookieStroe deleteCookie:cookie completionHandler:^{
            NSLog(@"Cookie を削除する");
        }]; // Cookie の取得、設定、削除の例

    iOS 11 API WKHTTPCookieStore を使用して、WKWebView が最初のリクエストで Cookie を送信しない問題を解決する

  • 問題の説明: 一部の HTML5 アプリのログイン状態の検証は Cookie に依存しています。ただし、WKWebView のリクエストには Cookie が自動的に含まれません。たとえば、ネイティブ レイヤーでログイン操作を実行し、Cookie 情報を取得して NSHTTPCookieStorage を使用してローカルに保存した場合、WKWebView を使用して対応する Web ページを開くと、まだログアウト状態になります。WebView でもログイン操作が実行される場合、この問題は発生しません。

  • この問題を解決するには、iOS 11 の API を使用できます。WKWebView に送信される各リクエストには WKHTTPCookieStore の Cookie が含まれていますが、NSHTTPCookieStorage の Cookie は含まれていない場合があります。したがって、WKWebView に送信される最初のリクエストには Cookie が含まれていません。

  • トラブルシューティング方法:

    -[WKWebView loadRequest:] を実行する前に、NSHTTPCookieStorage から WKHTTPCookieStore にコンテンツをコピーして、WKWebView Cookie の挿入を実現します。次のサンプルコードは例を示しています。

    [self copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:^{
                NSURL *url = [NSURL URLWithString:@"https://www.v2ex.com"];
                NSURLRequest *request = [NSURLRequest requestWithURL:url];
                [_webView loadRequest:request];
    }]; // NSHTTPCookieStorage から WKHTTPCookieStore へのコピー
    - (void)copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:(nullable void (^)())theCompletionHandler; { // Cookie のコピー処理
        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
        WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
        if (cookies.count == 0) {
            !theCompletionHandler ?: theCompletionHandler();
            return;
        }
        for (NSHTTPCookie *cookie in cookies) {
            [cookieStroe setCookie:cookie completionHandler:^{
                if ([[cookies lastObject] isEqual:cookie]) {
                    !theCompletionHandler ?: theCompletionHandler();
                    return;
                }
            }];
        }
    } // Cookie のコピー処理
    説明

    これは iOS 11 の API です。iOS 11 より前のシステムでは、異なる方法で処理する必要があります。

    iOS 11 より前の API を使用して、WKWebView が最初のリクエストで Cookie を送信しない問題を解決する

    すべての WKWebView オブジェクトは同じ WKProcessPool インスタンスを使用して、WKWebView オブジェクトが Cookie を共有できるようにします。Cookie はセッション Cookie と永続 Cookie に分類されます。ただし、アプリプロセスが強制終了されて再起動された後、WKProcessPool インスタンスはリセットされ、WKProcessPool 内の Cookie とセッション Cookie データが失われます。現在、WKProcessPool インスタンスをローカルに保存することはできません。ヘッダーに Cookie を入れる方法を使用できます。

     WKWebView * webView = [WKWebView new]; 
     NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
     [request addValue:@"skey=skeyValue" forHTTPHeaderField:@"Cookie"]; // Cookie をヘッダーに追加
     [webView loadRequest:request]; // リクエストの送信

    Cookie 値 skey=skeyValue は、ドメインを介して均一に取得することもできます。取得方法は、次のユーティリティクラスを参照できます。

    ALIDNSCookieManager.h
    
    #ifndef ALIDNSCookieManager_h
    #define ALIDNSCookieManager_h
    
    // URL に一致する Cookie ルール
    typedef BOOL (^ALIDNSCookieFilter)(NSHTTPCookie *, NSURL *);
    
    @interface ALIDNSCookieManager : NSObject
    
    + (instancetype)sharedInstance;
    
    /**
     URL に一致する Cookie ポリシーを指定する
    
     @param filter フィルター
     */
    - (void)setCookieFilter:(ALIDNSCookieFilter)filter;
    
    /**
     HTTP レスポンスによって送信された Cookie を処理して保存する
    
     @param headerFields HTTP ヘッダーフィールド
     @param URL 一致ポリシーに従って URL に関連付けられた Cookie を検索する
     @return ストレージに追加された Cookie を返す
     */
    - (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL;
    
    /**
     ローカル Cookie ストレージを照合し、対応する URL のリクエスト Cookie 文字列を取得する
    
     @param URL 一致ポリシーに従って関連付けられた Cookie を検索する URL を指定する
     @return 対応する URL のリクエスト Cookie 文字列を返す
     */
    - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL;
    
    /**
     保存されている Cookie を削除する
    
     @param URL 一致ポリシーに従って URL に関連付けられた Cookie を検索する
     @return 正常に削除された Cookie の数を返す
     */
    - (NSInteger)deleteCookieForURL:(NSURL *)URL;
    
    @end
    
    #endif /* ALIDNSCookieManager_h */
    
    ALIDNSCookieManager.m
    #import <Foundation/Foundation.h>
    #import "ALIDNSCookieManager.h"
    
    @implementation ALIDNSCookieManager
    {
        ALIDNSCookieFilter cookieFilter;
    }
    
    - (instancetype)init {
        if (self = [super init]) {
            /**
                ここで設定されている Cookie と URL の一致ポリシーは比較的単純で、URL.host に Cookie のドメインフィールドが含まれているかどうかを確認します
                setCookieFilter 操作を呼び出して Cookie の一致ルールを指定できます。
                たとえば、Cookie のドメインフィールドを URL.host のサフィックスと一致させるように設定できます | URL が Cookie のパス設定に準拠しているかどうか
                詳細な一致ルールについては、RFC 2965 セクション 3.3 を参照してください
             */
            cookieFilter = ^BOOL(NSHTTPCookie *cookie, NSURL *URL) {
                if ([URL.host containsString:cookie.domain]) {
                    return YES;
                }
                return NO;
            };
        }
        return self;
    }
    
    + (instancetype)sharedInstance {
        static id singletonInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (!singletonInstance) {
                singletonInstance = [[super allocWithZone:NULL] init];
            }
        });
        return singletonInstance;
    }
    
    + (id)allocWithZone:(struct _NSZone *)zone {
        return [self sharedInstance];
    }
    
    - (id)copyWithZone:(struct _NSZone *)zone {
        return self;
    }
    
    - (void)setCookieFilter:(ALIDNSCookieFilter)filter {
        if (filter != nil) {
            cookieFilter = filter;
        }
    }
    
    - (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL {
        NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:URL];
        if (cookieArray != nil) {
            NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
            for (NSHTTPCookie *cookie in cookieArray) {
                if (cookieFilter(cookie, URL)) {
                    NSLog(@"Cookie を追加: %@", cookie);
                    [cookieStorage setCookie:cookie];
                }
            }
        }
        return cookieArray;
    }
    
    - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL {
        NSArray *cookieArray = [self searchAppropriateCookies:URL];
        if (cookieArray != nil && cookieArray.count > 0) {
            NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray];
            if ([cookieDic objectForKey:@"Cookie"]) {
                return cookieDic[@"Cookie"];
            }
        }
        return nil;
    }
    
    - (NSArray *)searchAppropriateCookies:(NSURL *)URL {
        NSMutableArray *cookieArray = [NSMutableArray array];
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
            if (cookieFilter(cookie, URL)) {
                NSLog(@"適切な Cookie を検索: %@", cookie);
                [cookieArray addObject:cookie];
            }
        }
        return cookieArray;
    }
    
    - (NSInteger)deleteCookieForURL:(NSURL *)URL {
        int delCount = 0;
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
            if (cookieFilter(cookie, URL)) {
                NSLog(@"Cookie を削除: %@", cookie);
                [cookieStorage deleteCookie:cookie];
                delCount++;
            }
        }
        return delCount;
    }
    
    @end // ユーティリティクラスの例

    次のコードは、リクエストを送信する方法の例を示しています。

    WKWebView * webView = [WKWebView new]; 
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
    NSString *value = [[ALIDNSCookieManager sharedInstance] getRequestCookieHeaderForURL:url]; // Cookie 文字列の取得
    [request setValue:value forHTTPHeaderField:@"Cookie"]; // Cookie をリクエストヘッダーに設定
    [webView loadRequest:request]; // リクエストの送信

    リクエストを受信して処理する方法の例:

     NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (!error) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                // HTTP レスポンスヘッダーを解析し、Cookie を保存する
                [[ALIDNSCookieManager sharedInstance] handleHeaderFields:[httpResponse allHeaderFields] forURL:url]; // Cookie の処理と保存
            }
        }];
        [task resume]; // リクエストの処理と Cookie の保存

    document.cookie を使用して Cookie を設定し、後続のページ (同じドメイン) の Ajax および iframe リクエストの Cookie 問題を解決します。

    WKUserContentController* userContentController = [WKUserContentController new]; 
    WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'skey=skeyValue';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; // Cookie スクリプトの作成
    [userContentController addUserScript:cookieScript]; // Cookie スクリプトの追加

    Cookie に動的 IP アドレスが含まれているため、ログインに失敗する

    クライアントのログインセッション中に Cookie が生成され、関連するドメイン名に複数の IP アドレスが指定されている場合、ドメイン名を使用してクライアントにアクセスするときにドメイン名に対応する Cookie が読み取られ、IP アドレスを使用してクライアントにアクセスするときに IP アドレスに対応する Cookie が読み取られます。この場合、クライアントにアクセスするために使用したドメイン名に 2 回連続して異なる IP アドレスが指定されていると、ログインセッションが無効になります。

    アプリの WebView ページでシステム Cookie に保存されているログインセッションを使用する必要がある場合、以前はアプリ内のすべてのローカルネットワークリクエストはドメイン名を使用してアクセスし、Cookie 内のログインセッションを共有できました。しかし、HTTPDNS を使用した後、ローカルネットワークリクエストが IP アドレスを使用するようになったため、引き続きドメイン名を使用してアクセスする WebView はシステム Cookie に保存されているログインセッションを読み取ることができません (システム Cookie は IP アドレスに対応しています)。IP アドレスベースの直接接続が確立されると、サーバーは動的 IP アドレスを含む Cookie を返します。その結果、ログインに失敗します。

    IP アドレスを使用してアクセスした後、サーバーから返される Cookie も IP アドレスを使用します。これにより、対応するドメイン名でアクセスするとき、または同じドメイン名に属する異なる IP アドレスでアクセスするときに、ローカル Cookie を使用できなくなる可能性があり、Cookie が一致しないため、ログインに失敗します。

    提案されるアプローチは次のとおりです。

  • ドメイン名に基づいて Cookie ストレージに介入する必要があります。

  • ソースでは、API ドメイン名は単一の IP アドレスを返します。

    2 番目の方法では、DNS スケジューリング特性が失われる可能性があります。この方法は推奨されません。最初の方法の方が適しています。

    iOS 11 API WKHTTPCookieStore を使用して WKWebView の Cookie 管理問題を解決する

    サーバーが Cookie を返すたびに、IP アドレスをドメイン名に置き換えることによって、保存前に Cookie を変更します。これにより、ネットワークリクエストを受信するたびに IP アドレスを使用して Web ページにアクセスする場合でも、ローカル Cookie が一致します。これは、ホストフィールドの値が IP アドレスに対応するドメイン名に手動で変更されるためです。

    ネットワークリクエストが成功するか、Web ページが読み込まれた後、Cookie のドメインフィールドの IP アドレスをドメイン名に変更できます。サンプルコード:

    - (void)updateWKHTTPCookieStoreDomainFromIP:(NSString *)IP toHost:(NSString *)host { // Cookie のドメイン更新
        WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
        [cookieStroe getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            [[cookies copy] enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull cookie, NSUInteger idx, BOOL * _Nonnull stop) {
                if ([cookie.domain isEqualToString:IP]) {
                    NSMutableDictionary<NSHTTPCookiePropertyKey, id> *dict = [NSMutableDictionary dictionaryWithDictionary:cookie.properties];
                    dict[NSHTTPCookieDomain] = host;
                    NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:[dict copy]];
                    [cookieStroe setCookie:newCookie completionHandler:^{
                        [self logCookies];
                        [cookieStroe deleteCookie:cookie
                                completionHandler:^{
                                    [self logCookies];
                                }];
                    }];
                }
            }];
        }];
    } // Cookie のドメイン更新

    iOS 11 では、Cookie 内のデータを置き換えることができる期間を判断するのに役立つ次の API も提供されています。

    @protocol WKHTTPCookieStoreObserver <NSObject>
    @optional
    - (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
    @end
    //WKHTTPCookieStore
    /*! @abstract Cookie ストアに WKHTTPCookieStoreObserver オブジェクトを追加します。
     @param observer 追加するオブザーバーオブジェクト。
     @discussion オブザーバーはレシーバーによって保持されません。
     無効になる前にオブザーバーの登録を解除するのはあなたの責任です。
     */
    - (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer; // オブザーバーの追加
    
    /*! @abstract Cookie ストアから WKHTTPCookieStoreObserver オブジェクトを削除します。
     @param observer 削除するオブザーバー。
     */
    - (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer; // オブザーバーの削除

    関数:

    @interface WebViewController ()<WKHTTPCookieStoreObserver>
    - (void)viewDidLoad {
        [super viewDidLoad];
        [NSURLProtocol registerClass:[WebViewURLProtocol class]];
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
        WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
        [cookieStroe addObserver:self]; // オブザーバーの追加
    
        [self.view addSubview:self.webView];
        //... ...
    }
    
    #pragma mark -
    #pragma mark - WKHTTPCookieStoreObserver デリゲートメソッド
    
    - (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore { // Cookie 変更時の処理
        [self updateWKHTTPCookieStoreDomainFromIP:CYLIP toHost:CYLHOST]; // Cookie のドメイン更新
    } // Cookie 変更時の処理

    -updateWKHTTPCookieStoreDomainFromIP メソッドの実装は上記で示されています。

    このソリューションでは、クライアントが IP→HOST マッピング関係を維持し、IP から HOST への逆引き参照を実行できる必要があります。これは、維持コストが高くなります。以下では、より一般的な方法を紹介します。これは、iOS 11 より前の処理方法でもあります。

    iOS 11 より前の処理方法: NSURLProtocal インターセプト後に Cookie ストレージを手動で管理する:

    手順: IP 置換を行うときに元の URL をヘッダーに保存します。

    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { // リクエストの正規化
        NSMutableURLRequest *mutableReq = [request mutableCopy];
        NSString *originalUrl = mutableReq.URL.absoluteString; // 元の URL を保存
        NSURL *url = [NSURL URLWithString:originalUrl];
        // API 操作を呼び出して IP アドレスを取得する。
        NSArray *array = [[DNSResolver share] getIpsByCacheWithDomain:domain andExpiredIPEnabled:YES];
        NSString *ip = array.firstObject;
        if (ip) {
            NSRange hostFirstRange = [originalUrl rangeOfString:url.host];
            if (NSNotFound != hostFirstRange.location) {
                NSString *newUrl = [originalUrl stringByReplacingCharactersInRange:hostFirstRange withString:ip];
                mutableReq.URL = [NSURL URLWithString:newUrl];
                [mutableReq setValue:url.host forHTTPHeaderField:@"host"];
                // originalUrl を追加して元の URL を保存する
                [mutableReq addValue:originalUrl forHTTPHeaderField:@"originalUrl"]; // 元の URL をヘッダーに追加
            }
        }
        NSURLRequest *postRequestIncludeBody = [mutableReq cyl_getPostRequestIncludeBody];
        return postRequestIncludeBody;
    } // リクエストの正規化

    次に、データを取得した後、Cookie を手動で管理します。

    - (void)handleCookiesFromResponse:(NSURLResponse *)response { // レスポンスからの Cookie 処理
        NSString *originalURLString = [self.request valueForHTTPHeaderField:@"originalUrl"]; // 元の URL を取得
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            NSDictionary<NSString *, NSString *> *allHeaderFields = httpResponse.allHeaderFields;
            if (originalURLString && originalURLString.length > 0) {
                NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:allHeaderFields forURL: [[NSURL alloc] initWithString:originalURLString]];
                if (cookies && cookies.count > 0) {
                    NSURL *originalURL = [NSURL URLWithString:originalURLString];
                    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:originalURL mainDocumentURL:nil]; // Cookie の設定
                }
            }
        }
    } // レスポンスからの Cookie 処理
    
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler { // リダイレクト処理
        NSString *location = response.allHeaderFields[@"Location"];
        NSURL *url = [[NSURL alloc] initWithString:location];
        NSMutableURLRequest *mRequest = [newRequest mutableCopy];
        mRequest.URL = url;
        if (location && location.length > 0) {
            if ([[newRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) {
                // POST を GET にリダイレクトする。
                mRequest.HTTPMethod = @"GET";
                mRequest.HTTPBody = nil;
            }
            [mRequest setValue:nil forHTTPHeaderField:@"host"];
            // リクエストに Cookie を含める。
            [self handleCookiesFromResponse:response]; // Cookie の処理
            [XXXURLProtocol removePropertyForKey:XXXURLProtocolHandledKey inRequest:mRequest];
            completionHandler(mRequest);
        } else{
           completionHandler(mRequest);
        }
    } // リダイレクト処理

    手順 3: リクエストが送信される前に、リクエストに Cookie を含めます。

    + (void)handleCookieWithRequest:(NSMutableURLRequest *)request { // リクエストへの Cookie の追加
        NSString* originalURLString = [request valueForHTTPHeaderField:@"originalUrl"]; // 元の URL を取得
        if (!originalURLString || originalURLString.length == 0) {
            return;
        }
        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
        if (cookies && cookies.count >0) {
            NSDictionary *cookieHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
            NSString *cookieString = [cookieHeaders objectForKey:@"Cookie"];
            [request addValue:cookieString forHTTPHeaderField:@"Cookie"]; // Cookie をリクエストヘッダーに追加
        }
    } // リクエストへの Cookie の追加
    
    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { // リクエストの正規化
        NSMutableURLRequest *mutableReq = [request mutableCopy];
    //...
         [self handleCookieWithRequest:mutableReq]; // Cookie の追加
        return [mutableReq copy];
    } // リクエストの正規化

HTTP 302 リクエスト

上記の Cookie ソリューションでは、302 リクエストの Cookie 問題を解決できません。たとえば、最初のリクエストが http://www.a.com の場合、リクエストヘッダーに Cookie を含めることで、そのリクエストの Cookie 問題を解決します。次に、ページは 302 状態コードで http://www.b.com にリダイレクトされます。この時点で、http://www.b.com リクエストは Cookie を送信しないため、アクセスできない可能性があります。もちろん、各ページジャンプの前にコールバック関数が呼び出されるためです。

 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler; // ナビゲーションアクションの決定

このコールバック関数で 302 リクエストをインターセプトし、リクエストをコピーし、リクエストヘッダーに Cookie を含め、リクエストを再読み込みできます。ただし、この方法では、ページ iframe 内のクロスドメインリクエストの Cookie 問題は解決できません。これは、-[WKWebView loadRequest:] が mainFrame リクエストの読み込みにのみ適しているためです。