WKWebView
is an important component introduced by Apple in iOS 8 that replaces theUIWebView
, which provides developers with high-performance and stable web display and interaction capabilities. In this article, we will take a deeper look atWKWebView
underlying architecture, key features, usage, and advanced functionality.
I. The underlying architecture of WKWebView
WKWebView
Based on the WebKit framework , using multi-process architecture , page rendering and JavaScript execution in an independent Web process , the advantage of doing so is that the main application process is isolated from the Web content process , which can significantly improve the stability and security of the application . Its architecture mainly includes the following parts:
1. Web content process
It is responsible for operations such as HTML parsing, CSS parsing, JavaScript execution, and page rendering. These operations are performed in separate processes, preventing web page crashes from affecting the entire application.
2. Network processes
Responsible for the management of network requests and the processing of cached data, obtaining web content from data sources and transferring it to the Web content process.
3. UI process
It is mainly responsible for the interaction with the user, such as receiving user input, sending messages to the Web content process, etc. The UI process and the Web content process transfer information through IPC (inter-process communication).
The figure below shows theWKWebView
The architecture of the schematic:
+------------------+ +------------------+
| | <------------> | |
| | UI processes | | Web content processes |
| | IPC communication | |
+------------------+ +------------------+
^ ^
| ^ ^ ^ ^ ^ ^ ^
v v
+------------------+ +------------------+
| | | | | | |
| | | | | | | | | | | |
+------------------+ +------------------+
Second, the basic use of WKWebView
1. Initialize WKWebView
To useWKWebView
, which first requires basic initialization and configuration work. UnlikeUIWebView
InitializationWKWebView
You need to specify its configuration attributes.
#import <WebKit/>
@interface ViewController ()
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Creating Configuration Objects
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// initializationWKWebView
= [[WKWebView alloc] initWithFrame: configuration:configuration];
// Setting the inner margin
[ addSubview:];
// Example of loading a web page
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
}
@end
2. Loading local documents
In addition to loading network resources, theWKWebView
Local files can also be loaded:
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
NSString *htmlContent = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
[ loadHTMLString:htmlContent baseURL:baseURL];
3. Navigation control
WKWebView
Provides a rich set of navigation controls to help us handle web page operations such as forward, backward and refresh:
// Refresh the current page
[ reload];
// Stop loading
[ stopLoading];
// Go back to the previous page
[ goBack];
// Go forward to the next page
[ goForward].
4. Access to web content
WKWebView
One of the powerful features is the ability to execute JavaScript code directly and get the return value:
[ evaluateJavaScript:@"" completionHandler:^(id result, NSError *error) {
if (!error) {
NSLog(@"Page title: %@", result);
}
}];
WKWebView proxy and callbacks
WKWebView
Two main proxy protocols are provided:WKNavigationDelegate
cap (a poem)WKUIDelegate
, which handle callbacks for navigation and user interface aspects, respectively.
1. WKNavigationDelegate
The protocol manages the process of loading web content, including events such as start, finish, and failure:
@interface ViewController () <WKNavigationDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Setting up a navigation agent
= self;
// Load page
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
}
// The page starts loading
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"The page starts loading");
}
// Contents start to return
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"Contents start to return");
}
// Page load complete
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"Page load complete");
}
// Page Load Failure
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"Page Load Failure,incorrect: %@", );
}
@end
2. WKUIDelegate
This protocol handles UI events in a web page, such as displaying the JavaScriptalert
、confirm
、prompt
Dialog box:
@interface ViewController () <WKUIDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Setting up the User Interface Agent
= self;
// Load page
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
}
// JavaScript alertcircle (i.e. draw a circle around sth)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"draw attention to sth." message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"recognize" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
// JavaScript confirmcircle (i.e. draw a circle around sth)
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"recognize" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"recognize" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"abolish" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
// JavaScript promptcircle (i.e. draw a circle around sth)
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"importation" message:prompt preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
= defaultText;
}];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"recognize" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *input = ;
completionHandler(input);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"abolish" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(nil);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
@end
Fourth, the advanced use of WKWebView
1. Interacting with JavaScript
pass (a bill or inspection etc)WKScriptMessageHandler
Agreement.WKWebView
It is possible to have a two-way interaction with JavaScript in a web page.
Prerequisite Configuration
It needs to be inWKWebViewConfiguration
Configure the content controller in theWKUserContentController
and register the JavaScript message handler:
@interface ViewController () <WKScriptMessageHandler>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
WKUserContentController *contentController = [[WKUserContentController alloc] init];
[contentController addScriptMessageHandler:self name:@"nativeHandler"];
= contentController;
= [[WKWebView alloc] initWithFrame: configuration:config];
[ addSubview:];
NSString *html = @"<html><body><button onclick=\"('Hello from JS!');\">Click Me</button></body></html>";
[ loadHTMLString:html baseURL:nil];
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([ isEqualToString:@"nativeHandler"]) {
NSLog(@"Received message from JS: %@", );
}
}
- (void)dealloc {
[ removeScriptMessageHandlerForName:@"nativeHandler"];
}
@end
This way, when a web button is clicked, JavaScript sends the message to the native code and triggers theuserContentController:didReceiveScriptMessage:
Callback.
2. Loading progress bar
By listening to theWKWebView
(used form a nominal expression)estimatedProgress
property, we can realize the progress bar display during page loading:
@interface ViewController ()
@property (nonatomic, strong) UIProgressView *progressView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// initializationWKWebView
= [[WKWebView alloc] initWithFrame:];
[ addSubview:];
// initialization进度条
= [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
= CGRectMake(0, 88, , 2);
[ addSubview:];
// heedestimatedProgresscausality
[ addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
// Load page
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
}
- (void)dealloc {
[ removeObserver:self forKeyPath:@"estimatedProgress"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
= ;
if ( >= 1.0) {
[UIView animateWithDuration:0.5 animations:^{
= 0.0;
}];
} else {
= 1.0;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end
3. Processing of file uploads
WKWebView
Support for file uploads is achieved by realizingUIDocumentPickerViewController
, we can customize the operation of uploading files:
@interface ViewController () <WKUIDelegate, UIDocumentPickerDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// initializationWKWebView
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
= [[WKWebView alloc] initWithFrame: configuration:configuration];
= self;
[ addSubview:];
// Load page
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
}
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@""] inMode:UIDocumentPickerModeOpen];
= self;
= ^(NSArray<NSURL *> * _Nonnull urls) {
completionHandler(urls);
};
[self presentViewController:documentPicker animated:YES completion:nil];
}
@end
V. Performance optimization of WKWebView
due toWKWebView
In practice, you may face performance issues, here are some suggestions for performance optimization:
1. Caching strategy
By using a proper caching strategy, you can avoid loading the same resources over and over again, thus increasing loading speed. If you useURLCache
Configuration:
NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:1024 * 1024 * 10
diskCapacity:1024 * 1024 * 50
diskPath:@"wkwebview_cache"];
[NSURLCache setSharedURLCache:urlCache];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[ loadRequest:request];
2. Asynchronous loading of resources
Avoiding synchronous loading of resources that causes the main thread to block can be handled using asynchronous loading:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSURL *url = [NSURL URLWithString:@"/resource"];
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
[ loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@""]];
});
});
3. Reduction of DOM operations
When frequent manipulation of the DOM is required, try to combine multiple operations into one to reduce the rendering burden on the engine:
function updateContent() {
let container = ('container');
let fragment = ();
for (let i = 0; i < 1000; i++) {
let div = ('div');
= `Item ${i}`;
(div);
}
(fragment);
}
VI. OC and JavaScript communication advanced
Yes, if it's just passing simple user information data, other than through theWKScriptMessageHandler
There are several other ways to pass data from the client (Objective-C/Swift) to JavaScript.
1、Through URL Scheme
This method is mainly used to pass the user information as a query parameter embedded in the URL when the page is loaded. This approach is suitable for passing data on the initial loading of the page.
// Building user information data
NSString *userInfo = @"userId=12345&userName=JohnDoe";
NSString *urlString = [NSString stringWithFormat:@"?%@", userInfo];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
This can be done in JavaScript with the Get query parameters.
2、Execute JavaScript by evaluateJavaScript
evaluateJavaScript:completionHandler:
is a simple and straightforward way to execute arbitrary JavaScript code on the client side and get the result via a callback.
// construct (sth abstract)JavaScriptcoding
NSString *userId = @"12345";
NSString *userName = @"JohnDoe";
NSString *jsCode = [NSString stringWithFormat:@"setUserInfo('%@', '%@');", userId, userName];
// fulfillmentJavaScriptcoding
[ evaluateJavaScript:jsCode completionHandler:^(id result, NSError *error) {
if (error) {
NSLog(@"Error: %@", );
}
}];
In the web page, you need to define the corresponding JavaScript function to receive this data:
<script>
function setUserInfo(userId, userName) {
("User ID: " + userId);
("User Name: " + userName);
// Other business logic
}
</script>
3. Through User Scripts
If you want to inject data at the initial stage of page loading, you can use theWKUserScript
to add JavaScript preprocessing.
// construct (sth abstract)JavaScriptcoding
NSString *userId = @"12345";
NSString *userName = @"JohnDoe";
NSString *scriptSource = [NSString stringWithFormat:@" = {userId: '%@', userName: '%@'};", userId, userName];
// Creating User Scripts
WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
// Adding User Scripts to the Configuration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[ addUserScript:userScript];
// Create and load WKWebView
= [[WKWebView alloc] initWithFrame: configuration:config];
NSURL *url = [NSURL URLWithString:@""];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[ loadRequest:request];
With the above method, the page is automatically injected with user information when it loads, and the page can be accessed directly from anywhere。
4. Adoption (not recommended)
Although it's not really recommended, we can also set up the Passing information to a web page. Below is an example:
NSString *userId = @"12345";
NSString *userName = @"JohnDoe";
NSString *cookieScript = [NSString stringWithFormat:@" = 'userId=%@; path=/'; = 'userName=%@; path=/';", userId, userName];
[ evaluateJavaScript:cookieScript completionHandler:^(id result, NSError *error) {
if (error) {
NSLog(@"Error: %@", );
}
}];
In a web page, you can use JavaScript to parse the Get user information.
function getCookie(name) {
let value = `; ${}`;
let parts = (`; ${name}=`);
if ( === 2) return ().split(';').shift();
}
let userId = getCookie('userId');
let userName = getCookie('userName');
("User ID: " + userId);
("User Name: " + userName);
option
The above methods have their own advantages and disadvantages, according to the actual use of the scene to choose the appropriate method:
- If you're passing data on initial load, it's easier to use a URL Scheme.
- If data needs to be passed at any time after the page is loaded, the
evaluateJavaScript:completionHandler:
Very flexible. - need to inject data just before the page loads.
WKUserScript
It's a good way to go. While the method can pass data, it is not recommended for sensitive information.
VII. Summary
WKWebView
Provides a modern web view solution with high performance and stability. By understanding its underlying architecture, mastering common and advanced usage, how to interact with JavaScript and handle various requirements in real applications, you can better realize complex web page loading and interaction functions, and improve the user experience and performance of your applications.