Use of iOS WKWebView -- API

WKWebView is a browser control in WebKit, a framework launched by apple after iOS 8. Its loading speed is much faster than UIWebView, but its memory occupancy rate decreases a lot. It also solves the problem of memory leakage when loading web pages Most current projects only need to be adapted to iOS 8, so it is necessary to replace the UIWebView in the project with WKWebView

The use of WKWebView mainly involves the following classes:

WKWebView
WKWebViewConfiguration
WKUserScript
WKUserContentController
WKWebsiteDataStore

And two agents:

WKNavigationDelegate
WKUIDelegate

1. WKWebView

1.1 common attributes

// Navigation agent
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// UI agent
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;

// The page title is generally obtained dynamically using KVO
@property (nullable, nonatomic, readonly, copy) NSString *title;
// Page loading progress is generally obtained dynamically using KVO
@property (nonatomic, readonly) double estimatedProgress;

// The list of pages that can be returned and the web pages that have been opened are somewhat similar to the viewControllers property of the navigationController
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

// Page url
@property (nullable, nonatomic, readonly, copy) NSURL *URL;
// Is the page loading
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// Can I return
@property (nonatomic, readonly) BOOL canGoBack;
// Can I move forward
@property (nonatomic, readonly) BOOL canGoForward;
// WKWebView inherits from UIView, so if you want to set some properties of scrollView, you need to configure this property
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
// Whether to allow the gesture to slide left to return to the previous level, similar to the left slide return of navigation control
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

//Customizing UserAgent will override the default value, which is valid after iOS 9
@property (nullable, nonatomic, copy) NSString *customUserAgent

1.2 some methods:

// Initialization method with configuration information
// Configuration configuration information
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
// Load request
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// Load HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// Return to the previous level
- (nullable WKNavigation *)goBack;
// To advance to the next level, you need to have opened it before you can advance
- (nullable WKNavigation *)goForward;
// Refresh page
- (nullable WKNavigation *)reload;
// Refresh the page according to the cache validity
- (nullable WKNavigation *)reloadFromOrigin;
// Stop loading page
- (void)stopLoading;
// Execute JavaScript code
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

2. WKWebViewConfiguration

// Use this property to execute JavaScript code to modify the behavior of the page
@property (nonatomic, strong) WKUserContentController *userContentController;

//***********The following properties generally do not need to be set
// Preference settings,  
//The minimum font size can be set, and whether js is allowed
//Whether to automatically open a new window through js
@property (nonatomic, strong) WKPreferences *preferences;
// Allow media files to play
@property (nonatomic) BOOL allowsAirPlayForMediaPlayback
// A multimedia type that requires user action to play
@property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
// Whether to play online with h5 video player or full screen with native player
@property (nonatomic) BOOL allowsInlineMediaPlayback;

3. WKUserContentController

WKUserContentController is a bridge between JavaScript and native. The main methods used are:

// JavaScript injection and native interaction protocol
// JS end can be accessed through window webkit. messageHandlers.< name>. PostMessage (< MessageBody >) sends a message
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// Remove the injected protocol and call it in the deinit method.
- (void)removeScriptMessageHandlerForName:(NSString *)name;

// Inject JavaScript code to be executed through WKUserScript
- (void)addUserScript:(WKUserScript *)userScript;
// Remove all injected JavaScript code
- (void)removeAllUserScripts;

Using the interaction protocol injected by WKUserContentController, you need to follow the WKScriptMessageHandler protocol and obtain the events and parameters passed by the JavaScript side in its protocol method:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

WKScriptMessage contains the delivered protocol name and parameters, which are mainly obtained from the following properties:

// Protocol name, that is, the name passed by the add method above
@property (nonatomic, readonly, copy) NSString *name;
// Parameters passed
@property (nonatomic, readonly, copy) id body;

4. WKUserScript

WKUserScript is used to add additional JavaScript code to be executed to the loaded page. It is mainly an initialization method:

/*
source: JavaScript code to execute
injectionTime: The added location is an enumeration
typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
    WKUserScriptInjectionTimeAtDocumentStart,
    WKUserScriptInjectionTimeAtDocumentEnd
} API_AVAILABLE(macosx(10.10), ios(8.0));

forMainFrameOnly: Do you want to join all frames or just the main frame
*/
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;

5. WKUIDelegate

This proxy method is mainly used to replace some bullets in JS with system bullets, such as warning box, selection box and input box. The following three proxy methods are mainly used:

/**
 webView Called when a warning box pops up in, there can only be one button

 @param webView webView
 @param message Prompt information
 @param frame Can be used to distinguish which window is called
 @param completionHandler Called when the warning box disappears and called back to JS
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"warning" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"I got it!" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    
    [alert addAction:ok];
    [self presentViewController:alert animated:YES completion:nil];
}

/** confirm method corresponding to js
 webView Called when the selection box pops up in, two buttons

 @param webView webView description
 @param message Prompt information
 @param frame Can be used to distinguish which window is called
 @param completionHandler Called when the confirmation box disappears and called back to JS. The parameter is the selection result: YES or NO
 */
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Please select" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"agree" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }];
    
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"disagree" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }];
    
    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}

/** prompt method corresponding to js
 webView Called when the input box pops up in, two buttons and an input box

 @param webView webView description
 @param prompt Prompt information
 @param defaultText Default prompt text
 @param frame Can be used to distinguish which window is called
 @param completionHandler Called when the input box disappears and called back to JS. The parameter is the input content
 */
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Please enter" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];

    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"Please enter";
    }];
    
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"determine" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {    
        UITextField *tf = [alert.textFields firstObject];        
        completionHandler(tf.text);
    }];
    
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"cancel" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(defaultText);
    }];
    
    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}

6. WKNavigationDelegate

// The action of determining navigation is usually used to deal with whether cross domain links can be navigated.
// WebKit restricts cross domain security check and does not allow cross domain. Therefore, we need to deal with links that cannot cross domain separately.
// However, for Safari, cross domain is allowed, so you don't have to deal with it.
// This is to decide whether to Request
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //  Decide whether to jump before sending the request
    decisionHandler(WKNavigationActionPolicyAllow);  
}

// Receive response
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    // After receiving the response, decide whether to jump and send the request, which is allowed to be used together
    decisionHandler(WKNavigationResponsePolicyAllow);
}

//The API used for authorization verification is the same as that of AFN and UIWebView
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}

// Called when the navigation of the main frame starts a request
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
   
}

// Called when the main frame receives a service redirect
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    // Call after receiving the server jump request
}

// When the main frame fails to load data, it will call back
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

}

// Called when the content begins to return
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{  
}

//When the main frame navigation is completed, it will call back
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    // After the page is loaded, it is called.
}

// When the main frame fails to download data at last, it will call back
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}

// When the web content processing is completed, a callback is sent
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
}

7. WKWebsiteDataStore

WKWebsiteDataStore provides data types that websites can use, including cookies, hard disk cache, memory cache, data persistence and local persistence in some WebSQL. Relevant settings can be made through the property websiteDataStore of WKWebViewConfiguration class. The API related to WKWebsiteDataStore is also relatively simple:

// Default data store
+ (WKWebsiteDataStore *)defaultDataStore;

// If this data Store is set for webView, no data cache will be written to the file
// You can use this when you need to implement private browsing
+ (WKWebsiteDataStore *)nonPersistentDataStore;

// Is it cacheable, read-only
@property (nonatomic, readonly, getter=isPersistent) BOOL persistent;

// Get all available data types
+ (NSSet<NSString *> *)allWebsiteDataTypes;

// Finds cached data of the specified type
// The value of the callback is a collection of wkwebsitedatarecords
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;

// Delete the specified record
// The parameters here are obtained through the WKWebsiteDataRecord instance found in the above method
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;

// Delete a type of data modified after a certain time
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;

// Saved HTTP cookies
@property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore

PS: there are some differences in the method of obtaining WKWebsiteDataStore instance defaultdatastore / nonpersistent datastore. See this article for details iOS11. 3. Wkwebview clears the hole trodden by the cookie

dataType

// Hard disk cache
WKWebsiteDataTypeDiskCache,

// HTML offline web application caching
WKWebsiteDataTypeOfflineWebApplicationCache,

// Memory cache
WKWebsiteDataTypeMemoryCache,

// Local cache
WKWebsiteDataTypeLocalStorage,

// cookies
WKWebsiteDataTypeCookies,

// HTML session storage
WKWebsiteDataTypeSessionStorage,

//  IndexedDB database
WKWebsiteDataTypeIndexedDBDatabases,

// WebSQL database
WKWebsiteDataTypeWebSQLDatabases

WKWebsiteDataRecord

// Display name, usually domain name
@property (nonatomic, readonly, copy) NSString *displayName;

// Data types included
@property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;

WKHTTPCookieStore
For cookies, an instance object of wkhttpcookie store can be obtained from the httpcookie store attribute of the instance object of WKWebsiteDataStore. Through this object, we can perform relevant operations on cookies, and the officially provided API is not difficult to understand:

/*!  Find all stored cookie s
 */
- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;

/*! Save a cookie. After saving successfully, a callback method will be used
 */
- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;

/*! Delete a cookie. The cookie object to be deleted can be obtained through the 'getAllCookies' method
 */
- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;

/*! To add an observer, you need to follow the protocol WKHTTPCookieStoreObserver 
When the cookie is sent and changed, the observer will be notified through the protocol method of WKHTTPCookieStoreObserver. The observer needs to be removed after use
 */
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;

/*! Remove observer
 */
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;

WKHTTPCookieStoreObserver protocol method

@protocol WKHTTPCookieStoreObserver <NSObject>
@optional
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
@end

Simple application
Delete all types of data at the specified time

NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
    // Done
    NSLog(@"release");
}];

Find delete

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
    for (WKWebsiteDataRecord *record in records) {
        [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
            // done
        }];
    }
}];

Find and delete specific content

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
    for (WKWebsiteDataRecord *record in records) {
        if ([record.displayName isEqualToString:@"baidu"]) {
            [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                // done
            }];
        }
    }
}];

Add cookie

NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{
                              NSHTTPCookieName: @"liuhuofeitong",
                              NSHTTPCookieValue: @"2018",
                              NSHTTPCookieDomain: @"baidu.com",
                              NSHTTPCookiePath: @"/",
                              NSHTTPCookieExpires : [NSDate dateWithTimeIntervalSinceNow:60*60*24]
                        }];
    
if (@available(iOS 11.0, *)) {
    [web.webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:^{       
    }];
} else {
    // Fallback on earlier versions
}

Get cookie

if (@available(iOS 11.0, *)) {
    [webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
        NSLog(@"%@", cookies);
    }] ;
    
} else {
    // Fallback on earlier versions
}

Keywords: iOS objective-c

Added by digitalecartoons on Wed, 29 Dec 2021 03:07:25 +0200