All Products
Search
Document Center

Alibaba Cloud DNS:Best practices for integrating Alibaba Cloud Public DNS SDK in iOS WebView

Last Updated:Jun 01, 2023

This topic describes the issues that may occur when you integrate Alibaba Cloud Public DNS SDK in iOS WebView and provides solutions.

Overview

When iOS WebView loads network requests, iOS can intercept network requests by using a system API operation and perform custom logic injection. However, after intercepting network requests of WebView, iOS needs to handle the logic that is used to send IP address-based network requests, receive data, redirect pages, decode pages, manage cookies, and cache data. The requirements for using the SDK to resolve a domain name and using the obtained IP address to initiate a network request are high. In this scenario, the mobile operating system (OS) supports coarse-grained capabilities and defects exist. In this case, you need to be able to use the network or OS framework code to prevent the preceding issue and optimize related features.

Important

UIWebView is no longer supported by Apple, and WKWebView does not provide a developed solution for initiating network requests by using IP addresses. In this case, you can use only a private API operation to register a custom protocol and intercept network requests. However, a private API operation may be rejected by Apple. Exercise caution when you use a private API operation to register a custom protocol and intercept network requests.

Use UIWebView with NSURLProtocol to intercept network requests

Overview

  • NSURLProtocol can intercept the network requests that are initiated based on the upper-layer network library NSURLConnection or NSURLSession in iOS. NSURLProtocol can also intercept the requests that are initiated by UIWebView.

  • You can use the following API operation to register a custom NSURLProtocol. The custom NSURLProtocol is used to intercept upper-layer network requests of UIWebView, create network requests to handle the logic that is used to send network requests, receive data, and redirect pages, and return the results to the senders of the original network requests.

    [NSURLProtocol registerClass:[CustomProtocol class]];
  • Process of domain name resolution that is performed based on a custom NSURLProtocol:

    • A request for domain name resolution in canInitWithRequest is intercepted.

    • After the request is intercepted, Alibaba Cloud Public DNS performs domain name resolution.

    • After the domain name resolution is completed, the value of the host field in the URL and the value of the host field in the header of the HTTP request are both changed to the obtained IP address, and the system handles the logic that is used to send data, receive data, and redirect requests. This process is similar to the process for handling a common request.

    • The request processing result is sent to the sender of the UIWebView request by using the API of NSURLProtocol.

Use WKWebView with a private API operation to register a custom protocol and intercept requests

Overview

WKWebView processes network requests in a process that does not affect the app processes. The request data is not processed in the main process. Therefore, NSURLProtocol on WKWebView cannot be used to directly intercept requests.

When you use WKWebView, no developed solution for initiating network requests by using IP addresses is available. The following sample code shows how to use a private API operation to register a custom protocol and intercept requests.

  // Register your protocol.
  [NSURLProtocol registerClass:[CustomProtocol class]];
  // Create 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];
  // Register the scheme.
  Class cls = NSClassFromString(@"WKBrowsingContextController");
  SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
  if ([cls respondsToSelector:sel]) {
      // Allow HTTP requests or HTTPS requests, and allow other schemes that meet the requirement of the URL loading system.
      [cls performSelector:sel withObject:@"http"];
      [cls performSelector:sel withObject:@"https"];
  }

For more information about how to use NSURLProtocol, see Apple NSURLProtocol API. To view the sample code provided by Apple, see Apple Sample Code - CustomHTTPProtocol.

We recommend that you use the +load method to register the scheme. If you register the scheme by using - (void)viewDidLoad, an error may occur because the registration is not performed within the expected period of time.

Handle issues related to cookies when you use WKWebView to initiate network requests by using IP addresses

This section provides solutions to the following issues:

  • WKWebView is not optimal for cookie management. Developers wonder whether cookie management is optimized in WKWebVIew in iOS 11 and how to use the optimized cookie management feature.

  • When you use an IP address to initiate a network request, the value of the Domain field in the cookies that are returned by the server is also an IP address. If the IP address is dynamic, the following issue may occur: The logon status verification of some HTML5 apps depends on cookies. However, requests from WKWebView do not automatically carry cookies.

    No cookies are obtained when WKWebView uses NSURLProtocol to intercept requests

    iOS 11 provides a new API operation WKHTTPCookieStore, which can be used to intercept cookies of WKWebView.

    The following code provides an example on how to use WKHTTPCookieStore:

    WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
       // get cookies
        [cookieStroe getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            NSLog(@"All cookies %@",cookies);
        }];
    
        // set 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(@"set cookie");
        }];
    
        // delete cookie
        [cookieStroe deleteCookie:cookie completionHandler:^{
            NSLog(@"delete cookie");
        }];

    Use the WKHTTPCookieStore operation of iOS 11 to resolve the issue that the first request from WKWebView does not carry cookies

  • Problem description: The logon status verification of some HTML5 apps depends on cookies. However, requests from WKWebView do not automatically carry cookies. For example, you use your local native framework to log on to a web page, obtain cookies, and use NSHTTPCookieStorage to store the cookies to your on-premises machine. However, when you use WKWebView to access the related web page, you are not automatically logged on to the web page. If the logon operation is also performed on WebView, this issue does not occur.

  • You can use the API operation of iOS 11 to resolve this issue. Each request from WKWebView carries cookies in WKHTTPCookieStore but may not carry cookies in NSHTTPCookieStorage. Therefore, the first request from WKWebView does not carry cookies.

  • Solution:

    Before you perform the -[WKWebView loadReques:] operation, copy the content in NSHTTPCookieStorage to WKHTTPCookieStore. This way, cookies in WKWebView are injected into WKHTTPCookieStore. The following code provides an example:

    [self copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:^{
                NSURL *url = [NSURL URLWithString:@"https://www.v2ex.com"];
                NSURLRequest *request = [NSURLRequest requestWithURL:url];
                [_webView loadRequest:request];
    }];
    - (void)copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:(nullable void (^)())theCompletionHandler; {
        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;
                }
            }];
        }
    }

    The preceding sample code shows how to use the API operation of iOS 11 to resolve this issue. If you use an iOS version earlier than iOS 11, use the API operation of iOS earlier than iOS 11.

    Use the API operation of iOS earlier than iOS 11 to resolve the issue that the first request from WKWebView does not carry cookies

    All WKWebView objects use the same WKProcessPool instance to allow WKWebView objects to share cookies. Cookies are classified into session cookies and persistent cookies. However, the WKProcessPool instance of WKWebView is reset after the app kills processes and restarts. As a result, the persistent cookies and session cookies in WKProcessPool are lost. The WKProcessPool instance cannot be hosted on your on-premises machine. To resolve this issue, you can include cookies in the request header.

     WKWebView * webView = [WKWebView new]; 
     NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
     [request addValue:@"skey=skeyValue" forHTTPHeaderField:@"Cookie"]; 
     [webView loadRequest:request];

    You can use the Domain field to obtain the cookie value skey=skeyValue. The following example shows the custom class.

    ALIDNSCookieManager.h
    
    #ifndef ALIDNSCookieManager_h
    #define ALIDNSCookieManager_h
    
    // Make sure that the specified URL matches the cookie rule.
    typedef BOOL (^ALIDNSCookieFilter)(NSHTTPCookie *, NSURL *);
    
    @interface ALIDNSCookieManager : NSObject
    
    + (instancetype)sharedInstance;
    
    /**
     Specify a URL that matches the cookie rule.
    
     @param filter Specify the filter.
     */
    - (void)setCookieFilter:(ALIDNSCookieFilter)filter;
    
    /**
     Process and store cookies that are carried in HTTP responses.
    
     @param headerFields HTTP Header Fields
     @ param URL Search for the URL that matches the cookies based on the matching rule.
     @ return Return the cookies that are added to the storage.
     */
    - (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL;
    
    /**
     Match the local cookies and obtain the request cookies that match the URL.
    
     @ param URL Search for the URL that matches the cookies based on the matching rule.
     @ return Return the request cookies that match the URL.
     */
    - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL;
    
    /**
     Delete the stored cookies.
    
     @ param URL Search for the URL that matches the cookies based on the matching rule.
     @ return Return the number of cookies that are deleted.
     */
    - (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]) {
            /**
                In this example, you can check whether the value of the host field in the URL includes the value of the Domain field in cookies to determine whether the URL matches the cookies.
                You can call the setCookieFilter operation to specify the cookie matching rule. 
                For example, you can specify the matching rule that the Domain field in cookies matches the suffix of the host field in the URL. You can also check whether the URL complies with the configuration of the Path field in cookies.
                For more information about the matching rule, see section 3.3 of RFC 2965.
             */
            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(@"Add a 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(@"Search an appropriate 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(@"Delete a cookie: %@", cookie);
                [cookieStorage deleteCookie:cookie];
                delCount++;
            }
        }
        return delCount;
    }
    
    @end

    The following code provides an example on how to send requests:

    WKWebView * webView = [WKWebView new]; 
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
    NSString *value = [[ALIDNSCookieManager sharedInstance] getRequestCookieHeaderForURL:url];
    [request setValue:value forHTTPHeaderField:@"Cookie"];
    [webView loadRequest:request];

    The following code provides an example on how to receive and process a request:

     NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (!error) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                // Parse the HTTP response header and store cookies.
                [[ALIDNSCookieManager sharedInstance] handleHeaderFields:[httpResponse allHeaderFields] forURL:url];
            }
        }];
        [task resume];

    Configure document.cookie to resolve the cookie-related issue that occurs when you send Asynchronous JavaScript and XML (AJAX) or iframe requests to web pages that use the same domain name.

    WKUserContentController* userContentController = [WKUserContentController new]; 
    WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'skey=skeyValue';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; 
    [userContentController addUserScript:cookieScript];

    Logon failure caused by dynamic IP addresses in cookies

    If cookies are generated during a client logon session and multiple IP addresses are specified for the related domain name, cookies that correspond to the domain name are read when the domain name is used to access the client, and cookies that correspond to an IP address are read when the IP address is used to access the client. In this case, if different IP addresses are specified for the domain name that you use to access the client two consecutive times, the logon session becomes invalid.

    If the WebView page in an app needs to use the logon session that is stored in system cookies, all local network requests of the app can share the logon session of cookies when a domain name is used to access a web page. However, when the local network requests use HTTPDNS, the requests need to use an IP address to access the client. As a result, WebView that still uses domain names to access web pages cannot read the logon session that is stored in system cookies. The key in the system cookies corresponds to the IP address. After a network request is initiated by using an IP address, the server returns cookies that include a dynamic IP address. As a result, the logon fails.

    After an IP address is used to access a web page, the cookies that are returned by the server also include the IP address. Therefore, when you use a domain name to access a web page, local cookies cannot be used or another IP address that corresponds to the same domain name may be used to access the web page. In this case, the cookies that are used are different. As a result, the logon fails.

    You can resolve this issue by using one of the following methods:

  • Change the storage mode of cookies to ensure that cookies are stored based on domain names.

  • Make sure that only a single IP address is returned for the domain name.

    We recommend that you use the first method because you cannot use the scheduling capability of DNS in the second method.

    Use the WKHTTPCookieStore operation of iOS 11 to resolve cookie management issues of WKWebView

    Each time the server returns cookies, use a domain name to replace the IP address that is included in cookies before cookies are saved. This way, local cookies are matched even if an IP address is used to access a web page each time a network request is received. This is because the value of the host field is manually changed to a domain name that corresponds to the IP address.

    The following code provides an example on how to use a domain name to replace the IP address included in cookies after a request is successful or a web page is loaded:

    - (void)updateWKHTTPCookieStoreDomainFromIP:(NSString *)IP toHost:(NSString *)host {
        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];
                                }];
                    }];
                }
            }];
        }];
    }

    iOS 11 also provides operations shown in the following code to help you determine the period of time when data in cookies can be replaced:

    @protocol WKHTTPCookieStoreObserver <NSObject>
    @optional
    - (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
    @end
    //WKHTTPCookieStore
    /*! @abstract Adds a WKHTTPCookieStoreObserver object with the cookie store.
     @param observer The observer object to add.
     @discussion The observer is not retained by the receiver. It is your responsibility
     to unregister the observer before it becomes invalid.
     */
    - (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;
    
    /*! @abstract Removes a WKHTTPCookieStoreObserver object from the cookie store.
     @param observer The observer to remove.
     */
    - (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;

    The following code provides an example on how to use the operations:

    @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 Delegate Method
    
    - (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore {
        [self updateWKHTTPCookieStoreDomainFromIP:CYLIP toHost:CYLHOST];
    }

    The usage of the -updateWKHTTPCookieStoreDomainFromIP method is described in the preceding section.

    To use this solution, you must maintain the mapping between IP addresses and domain names for clients. The mapping helps you identify a domain name based on an IP address. The maintenance cost of the mapping may be high. The following solution is a general solution, which is used for iOS earlier than iOS 11.

    Manually manage cookies in requests intercepted by NSURLProtocol

    Step 1: Include the original URL in the request header when you replace an IP address with a domain name.

    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
        NSMutableURLRequest *mutableReq = [request mutableCopy];
        NSString *originalUrl = mutableReq.URL.absoluteString;
        NSURL *url = [NSURL URLWithString:originalUrl];
        // Call an API operation to obtain the IP address.
        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"];
                // Include originalUrl in the request header and save the original URL.
                [mutableReq addValue:originalUrl forHTTPHeaderField:@"originalUrl"];
            }
        }
        NSURLRequest *postRequestIncludeBody = [mutableReq cyl_getPostRequestIncludeBody];
        return postRequestIncludeBody;
    }

    Step 2: After the data is obtained, manually manage cookies.

    - (void)handleCookiesFromResponse:(NSURLResponse *)response {
        NSString *originalURLString = [self.request valueForHTTPHeaderField:@"originalUrl"];
        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];
                }
            }
        }
    }
    
    - (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"]) {
                // Redirect POST to GET.
                mRequest.HTTPMethod = @"GET";
                mRequest.HTTPBody = nil;
            }
            [mRequest setValue:nil forHTTPHeaderField:@"host"];
            // Include the cookies in the request. 
            [self handleCookiesFromResponse:response];
            [XXXURLProtocol removePropertyForKey:XXXURLProtocolHandledKey inRequest:mRequest];
            completionHandler(mRequest);
        } else{
           completionHandler(mRequest);
        }
    }

    Step 3: Before the request is sent, include the cookies in the request.

    + (void)handleCookieWithRequest:(NSMutableURLRequest *)request {
        NSString* originalURLString = [request valueForHTTPHeaderField:@"originalUrl"];
        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"];
        }
    }
    
    + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
        NSMutableURLRequest *mutableReq = [request mutableCopy];
    //...
         [self handleCookieWithRequest:mutableReq];
        return [mutableReq copy];
    }

Handle issues related to the cookies of HTTP 302 requests

The preceding cookie-related solutions cannot be used to resolve issues that occur when the HTTP 302 response code is returned for requests. For example, if the first request is to access http://www.a.com, you can include cookies in the request header to resolve the cookie-related issue. When the web page for the request is redirected to http://www.b.com, the request may fail because the request does not include cookies. The callback function is called each time the system redirects the user to another web page.

 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

Therefore, you can use the callback function to intercept a request for which the HTTP 302 response code is returned, copy the request, include cookies in the request header, and then call loadRequest. However, this solution cannot resolve the cookie-related issue that occurs when cross-origin iframe for HTTP requests are initiated. -[WKWebView loadRequest:] is suitable only for loading mainframe requests.