A background
Students familiar with iOS\macOS Hybrid development should have experienced that although WKWebView is a “new” component launched by Apple as a replacement for UIWebView\WebView, most developers really “can not love it”. After all, for most of the domestic application developers, in the actual use of WKWebView’s so-called “advantages” may not be reflected, but the “pit” is really not shallow.
At present, the community or online WKWebView related materials can be found, most of them are outdated and copycat, copy-paste type. A small percentage of real practice and exploration developers, perhaps due to time or energy, failed to elaborate on the problems and solutions. As a result, there are a lot of wKWebView-related information online, but the quality is not high; Moreover, there are many problems such as unclear background explanation and lack of effective verification of solutions.
I have been in the field of end container development for many years and have “fought” WKWebView several times in the design of production environment. Blend App development has been the standard at present, on the one hand, for such a long time usage experience summary, on the other hand also hope to return in the struggle of students to provide some new perspectives or solution, so ready to combine its part of the source, their understanding of the components and thus share part of the problem solution.
IOS file download /
This article tries to illustrate 3 things:
-
What are the typical problems in using WKWebView
-
Why do these problems arise
-
What are the solutions to these problems
IOS terminal network design and WKWebView design features we can refer to through the official information. However, in order to better explain the problem later, let’s focus on the following two basic knowledge points related to the following content of the article:
-
IOS network design and Cookie management
-
WKWebView multi-process model
Cookie management is often involved in the process of mixed development. In application development, we know that we can manage application cookies by NSHTTPCookie and NSHTTPCookieStorage. However, how to manage the Cookie at the system level and how to link with each module at the network layer is of vital importance for us to analyze the Cookie problem in WKWebView later. According to the official information, we know the relationship between iOS platform network modules is as follows:
The modules from top to bottom are as follows:
-
WebKit: The application layer, client App and WKWebView are located in this layer.
-
NSURL: it can be understood as the encapsulation and extension layer for the underlying CF interface. NSURLConnection and NSURLSession are in this layer.
-
CFNetwork: iOS network module core implementation layer, is the most important part of the network layer design. Responsible for network protocol assembly, sending and receiving and other major work, closely related to CoreFoundation framework.
-
BSD Socket: socket services based on underlying hardware interfaces.
CFNetwork is the core module of the entire network system, which is responsible for assembling requests and processing responses.
Core contents include:
-
CFURLRequest: Includes information about the URL, header, and body requests. The CFURLRequest is further converted to CFHTTPMessage.
-
CFHTTPMessage: The definition and conversion of the HTTP protocol, converting each request request into standard HTTP text.
-
CFURLConnection: Mainly handles request tasks. This includes pThread thread, CFRunloop, request queue management, and so on. Apis for start, Cancel, and so on are provided.
-
CFHost: responsible for DNS, has the function, such as CFHostStartInfoResolution dns_async_start and getaddrinfo_async_start based method.
-
CFURLCache/CFURLCredential/CFHTTPCookie: processing cache/certificates/cookie related logic, have corresponding NS class.
The key information can be seen from the above analysis: iOS Cookie management-related modules are in the CFNetwork layer. That is, the “set-cookie” field in the Response request is consumed and processed in CFNetwork. From official information, we know that a big change of WKWebView compared to UIWebView is the “multi-process model” :
When WKWebView is running, the core module runs in an independent process, independent from the App process.
WKWebView use in various problems, many and multi – process running mode has a great relationship. The multi-process model is explained in detail but what kind of multi-process is it? Let’s use a schematic diagram to illustrate:
-
WKWebView(WebKit) contains three types of processes: UI Process, Networking Process, and WebContent Process.
-
UI Process: the App Process in which some modules in WKWebView(WebKit) run and are responsible for starting other processes.
-
Networking Process: the Networking module Process, mainly responsible for WKWebView network request related functions; This process is only started once in the App and shared among multiple WkWebViews.
-
WebContent Process: the Web module Process, mainly responsible for the running of WebCore, JSCore related modules, is the core Process of WKWebView. This process will start multiple times in the App, and each WKWebView will have its own separate WebContent process.
-
The processes communicate with each other through the CoreIPC process.
In general: in a client App, multiple WkWebViews share a UI process (shared with the App process), a Networking process, and a WebContent process for each WKWebView instance. Example:
The official document here does not explain the startup rules of WebContent Process and Networking Process clearly, and the document is slightly different from the latest rules due to version iteration and other reasons. In order to avoid confusion and ambiguity, the following combined with WebKit source code for a little analysis.WebContent process startup rulesAccording to the official documentation:
A WKProcessPool object represents a single process that WebKit uses to manage web content. To provide a more secure and stable experience, WebKit renders the content of web views in separate processes, rather than in your app’s process space. By default, WebKit gives each web view its own process space until it reaches an implementation-defined process limit. After that, web views with the same WKProcessPool object share the same web content process.
The rule is that new processes are created first and shared when they come online beyond a certain threshold, controlled within WebKit by maximumProcessCount. However, this rule only applies to systems prior to iOS13. After iOS13, WKWebView starts a new WebContent Porcess every time it creates an instance. The implementation is as follows. IOS13 before:
IOS 13 and later:
Networking Process startup rules The Networking rules are relatively simple, and ensure that one application is started within the App life cycle. (A new application is created after a Crash.) Related codes:
Three main problems and Solutions In the use of WKWebView in the production environment, in addition to the relatively simple use and adaptation problems, easy to developers and front-end students caused by four problems:
-
Request proxy problem
-
Cookie Management Issues
-
Overall screen adaptation problem
-
WebContent process crash problem
The following is a description of the causes, possible solutions and problems introduced under different solutions for the four cases. Request proxy issues this should be the number one issue preventing WKWebView from rolling out. The background of the problem is relatively simple, not that there is any technical difficulty, but that Apple officially does not want WKWebView requests to be blocked by apps “for security”. However, in actual application scenarios, we need to proxy WebView requests to meet service and performance requirements. Typical scenarios include offline packets and traffic monitoring. Officially unsupported and with business scenarios, we had to try to solve it through “dark magic”. At present, there are two widely applicable solutions:
-
Through [WKBrowsingContextController registerSchemeForCustomProtocol:] to the registered agent, for the convenience of hereinafter referred to as the agent of plan 1.
-
Through [WKWebViewConfiguration setURLSchemeHandler: forURLScheme:] to register, for the convenience of agent solution 2 for short.
At present, the realization methods of the two solutions have abundant information and explanation, which will not be described here. Although these two schemes can “partially solve the problem” to some extent, they also bring relatively many side effects. How to choose in the production environment still needs to be decided by the specific developer. The following are respectively through the “agent scheme 1″ and” agent scheme 2″ to refer to a simple explanation, you can make a reference for selection. Proxy solution 1 This is the earliest WKWebView request proxy solution, which can meet the needs of iOS 9 and later applications (the latest version is iOS 14). According to the previous research and analysis, most apps with agency requirements in the industry adopt this scheme or variant scheme. 1) scheme ideas through WKBrowsingContextController HTTP (s) will be registered to the Networking m_registeredSchemes array. For Scheme in the array, WebKit will send the request to the App process through WKCustomProtocol, and the App process will perform the sending. Sends the data to the App from Networking process, intends to shed within its Body parts (see WebCoreArgumentCodersMac. Mm) :
So you need to do something special for the request that carries the body. The solution is to override the WebView request sending method by injecting a script into the WKWebView. The body part is serialized before the request is sent and then passed to the App process via bridge for temporary storage. App process agent WKWebView request, according to the rules according to the need to splice the cache body, after the completion of the send action. 2) Disadvantages of the scheme Although this scheme is widely applicable, its disadvantages are also obvious. There are two main aspects: (1) Problem 1: It can not be directed, but can only be a one-size-fits all, that is, if the App adopts this scheme, all requests sent to its WKWebView instance need proxy. If a WKWebView instance does not inject script or execute proxy, it may cause the request cannot be sent, send lack of body and other problems, common in some integrated two-party library, three-party library WKWebView instances. (2) Problem 2: It is difficult to guarantee the completeness of rewriting script. As the request sending logic needs to be rewritten in JS layer, such as the interface of form submission, AJAX and Fetch, the quality of rewriting interface directly determines the completeness of the scheme. And WKWebView original design has many capabilities in c++ level implementation, only in JS rewrite cannot guarantee alignment. The known problems are:
-
For synchronous requests, this scheme is not currently supported.
-
Support for streaming requests, such as upload scenarios, is currently poor. It can only be sent after the JS side has fully read the data.
-
Unable to process Fetch API Stream return value.
-
When a Form submission contains a large chunk of data, it may be lost or Crash.
Proxy scheme 2 the scheme is based on the apple on the iOS 11 open [WKWebViewConfiguration setURLSchemeHandler: forURLScheme:] do “extension” to implement the interface. This solution is useful for devices after iOS 11.3 (WebKit handles some of the Body delivery issues). 1) Scheme idea
-
[WKWebViewConfiguration setURLSchemeHandler: forURLScheme:] can register custom requests on the WKWebView instance Scheme. If the request sent by WKWebView matches the registration Scheme, it is proxyed to the UI process (App process) to perform the sending action.
-
WKWebView does not support registering standard schemes such as HTTP (S) internally by default, but there is “dark magic” to circumvent this restriction.
-
The body can also be lost when AJAX sends BLOB data, which can be solved by referring to a similar solution in proxy Solution 1.
2) Scheme Advantages Agent scheme 2 has two huge advantages over scheme 1:
-
No need to cut across the board, configuration and WKWebView instance binding: that is, we can be directed to deal with the WKWebView instance, for the object in the tripartite library, can do no impact, greatly improved security.
-
No need to rewrite all sent requests: In most cases, the body in the request can be carried into the App process, which means we only need to target some exceptions, greatly improving robustness.
3) Disadvantages of the scheme In addition to iOS 11.3 system version restrictions, there are also a lot of difficult problems in the specific operation, mainly as follows: (1) Problem 1: In the case of multi-image fragment download, there are bugs in the processing timing of WKWebView: When the large image is loaded in WKWebView and the large image data is returned in fragments, the abnormal timing processing in WKWebView may lead to problems such as the image cannot be displayed and the image display is incomplete. Specific can be combined with the process of loading pictures in WebKit to briefly illustrate:
The problem arises in the execution sequence of step1, step2 and step3 above. In abnormal cases, the execution sequence is occasionally shown as step1 -> step3 -> step2, and step3 is no longer triggered (allDataReceived), resulting in the final content of the image not being rendered on the screen. Solution: currently, no effective solution, by configuring suppressesIncrementalRendering configuration to YES, to some extent can alleviate the problem, but it cannot effect a radical cure and experience a little impact. (2) Problem 2: iOS 12 and the following system system synchronization AJAX causes Crash problem performance: In WKWebView if there is a Web page to send the sync request, can lead to the WebContent process collapse, WKWebView callback webViewWebContentProcessDidTerminate, This leads to problems such as a blank screen. This problem can be identified as an internal WebKit BUG, and has been related to Fix:
Bug1: WebURLSchemeHandlerProxy: : loadSynchronously crash with sync request (2018-08-06 14:14) : bugs.webkit.org/show_bug.cg…
Bug2: WKURLSchemeHandler crashes when sent errors with SYNC XHR (2019-06-20 01:20) : bugs.webkit.org/show_bug.cg…
Solution: For Bug1, the processing is relatively simple, that is, before the network request callback error, callback some empty data first to avoid the problem; But there is no effective solution for Bug2. Symptom: If a page is redirected using 301, the redirected page may fail to load, which leads to page exceptions and blank screen. Solution: Close processSwapsOnNavigation, which will be set to NO (internal property). In general, although agent scheme 2 has great advantages over agent scheme 1, the current usage of agent scheme 2 is slightly lower than that of agent scheme 1 due to version limitation and other reasons. Compared with proxy scheme 1, the advantages and disadvantages of this scheme are obvious. For example, the problem that images cannot be displayed in multi-image fragment scenes has not been found effective solutions. Whether plan 2 can be used in the production environment instead of Plan 1 needs to be considered by students themselves. 2 Cookie Problem According to official documents and materials, we know that WKWebView is “independent storage”, resulting in Cookie and Cache not communicating with App, thus causing problems. However, this expression is relatively vague, and in practice, WKWebView is not completely isolated from App cookies. Such ambiguous performance makes it difficult to make clear where the boundary of “pass” or “pass” lies. First of all, according to my own understanding of this, I will try to explain what is the problem of WKWebView using cookies and the reasons behind it. Because Apple does not open source all the code, there are many contents are their own understanding and inference, can not guarantee completely correct, only introduce part of the idea and judgment, for everyone in need of reference. Cookie management strategy According to the background introduction in the previous section, we know that iOS Cookie is managed by CFHTTPCookie and CFHTTPCookie at the CFNetwork layer, which is a part of the CFNetwork module. In addition, the system has different management policies for Session cookies and persistent cookies:
-
Session Cookie: saved in memory and takes effect within a process period. On iOS mobile terminals, an App process corresponds to a Session, that is, Session cookies can be shared within the process.
-
Persistent Cookie: This part of the Cookie is not only stored in memory, but also persisted to the disk and can be used multiple times. Local files are stored in a sandbox folder/Library/Cookies, Cookies, binarycookies; It is important to note that persistent Cookies are not immediately synchronized to Cookies. Binarycookies, as a rule of thumb, have a delay of 300ms ~ 3s.
Based on the iOS Cookie management in the previous section and combined with the multi-process model, we can probably infer the Cookie management model of App and WKWebView, as shown in the following diagram:
Note: WKHTTPCookieStore is drawn into the Networking Process. In reality, this module is scattered in the WebContent, Networking and UI processes, and parts of each Process are connected by IPC. According to the figure above, two core points related to WKWebView cookies can be guided: 1) What is the specific problem of WKWebView cookies
-
For “Session cookies “: App processes are completely isolated from WKWebView (WebContent + Networking) processes.
-
For “persistent Cookie” : There is time difference between App process and WKWebView process (WebContent + Networking).
2) Root cause of WKWebView Cookie problem
- Design of App process and Networking process.
The core goal is that once you understand the WKWebView problem and its root cause, how to handle it is relatively clear: depending on whether a network request is brokered by WKWebView, different processing strategies are required.
-
Scenario 1: No Proxy WKWebView Network request: Cookies are completely managed by the Networking process, and WKWebView can be self-closed. In most cases, App processes do not need to be aware. If awareness is needed, schemes such as JS bridge and forced persistence can be selected according to business scenarios.
-
Scenario 2 – Brokered WKWebView Network request: Cookies are mostly managed by the App process, which synchronization strategy should be adopted.
Since scenario 1 was not used in a production environment, this article does not attempt to do a rash analysis. The next part of the analysis focuses on scenario 2. Our core objectives in scenario 2:
-
Cookies generated in the App process can be synchronized to the Networking process in time: It mainly solves the problem of how to read related cookies in time on the JS side when there is “set-cookie” in Reponse.
-
Cookies generated by JS in WebContent can be synchronized to the App process in time: It mainly solves the problem of how to ensure that cookies generated by JS can be carried normally in subsequent network requests of proxy.
Before confirming the scheme, we should first clarify a question: what are the sources of client-side cookies? For App processes, there are two sources of cookies:
-
NSHTTPCookieStorage.
-
In the network request Response Header via “set-cookie”.
In the case of WebContent, JS is mainly written by document.cookie (set-cookie does not take effect in WKWebView after network proxy). Second, we need to confirm the available means for synchronization: For the system after iOS 11, Apple has provided us with the WKHTTPCookieStore object for reading and writing, listening to the Cookie corresponding to WKWebView, which can be directly used. For systems prior to iOS 11, this should be treated differently. The process for synchronizing the App process to the Networking process is as follows:
-
In step 1, the Session Cookie needs to be persisted and stored temporarily (note that it needs to be identified for recovery).
-
Step 2: Call NSHTTPCookieStorage internal interface _saveCookies to trigger forced synchronization.
-
Step 3: Restore temporarily saved Session cookies to avoid contamination.
Since the Networking process does not generate cookies, the next step is to synchronize cookies from the WebContent process: The processing strategy is that JS focuses on writing the document.cookie method, and when JS modifies cookies, cookies are passed to the App process through bridge. After clarifying the problems, objectives and available means, we can summarize the solution of WKWebView cookie-related problems:
-
For iOS 11 and later, we can HOOK NSHTTPCookieStorage to read and write cookies and listen for the “set-cookie” keyword in network requests. Synchronize to WKWebView when App process Cookie changes; At the same time, through the ability to provide cookiesDidChangeInCookieStore WKHTTPCookieStore to monitor the change of the Cookie in WKWebView.
-
For pre-ios 11 systems, the processing strategy is similar. But we need to use NSHTTPCookieStorage interface to do forcible synchronization, and need to pay attention to restore Cookie SessionOnly attribute; At the same time, we need to feel the change of cookies in WKWebView by writing document.cookie in JS.
Special attention: when using iOS 11 solution processing, it must be noted that the operation of WKHTTPCookieStore will involve IPC communication. If the communication is too frequent and the amount of communication data is too large, obvious performance problems will occur. In extreme cases, the IPC module may be abnormal and all WKWebViews may fail to be loaded. For example, in a typical scenario, if there are many requests on a page, and each request is with “set-cookie”, and for the simplicity of business, the Cookie of App process is fully synchronized to WKWebView every time, when there are too many cookies, there is a certain probability (violent test can be repeated) to trigger IPC exceptions. As a result, all subsequent WKWebView instances cannot be loaded normally and can be recovered only after the App kills the process. It is recommended that when synchronizing cookies, try to synchronize the changing parts as needed. 3 Overall screen adaptation The overall screen adaptation problem is relatively uncomplicated, but it is easy to cause some problems because WKWebView and UIWebView differ in performance. The problem is that UIWebView and WKWebView have slightly different performance in supporting the front-end viewport-fit: UIWebView has better support for viewport-Fit, and its performance is basically consistent with that expressed in the official document. However, there is an unspoken rule in WKWebView that viewport-fit=cover may not work if the height of the body in the Web page does not exceed the actual height of the WKWebView component. The solution is to circumvent this by setting the body height to 100vh (or something similar) on the page. 4 WebContent process crash This is a low probability, but lack of a general, effective solution to the problem. We know WKWebView multi-process mode, if the WebContent process because of various reasons appear Crash, WKWebView will told developers by webViewWebContentProcessDidTerminate callback, Normally we reload the page using the Reload method. At the same time, if the user device memory is insufficient, the system may actively KILL the WebContent process. That is, the App process (foreground) may be normal, but the WebContent crashes and the page reloads. For the most part, entering this process does not necessarily complicate user operations. However, if the memory crunch is caused by the front end triggering a service, such as a form evoking the camera to upload an image, the effect of this process on the user can be fatal. Even if we use WebView Reload to restore the page, the uploading action of the user will be interrupted, resulting in abnormal submission process and affecting the user’s operation. And if the user device enters this state, in most cases the same process will be triggered when the user tries again. In this case, the user cannot perceive the root cause of the problem in time, and the most intuitive response is: “There is a bug in the App!” Therefore, from the user’s point of view, the lack of automatic recovery, problem solving methods. At present, there is no effective and unified solution to this problem. One way to solve this problem is to configure the client side and the front end, and design a directional solution for the core and possibly abnormal processes. Data is persisted through the capability of the end and the side. After similar anomalies occur, persistent data is used to recover the site, and the user operation process is ensured as normal as possible without the user feeling.
The above is our end container design and development process, the use of WKWebView encountered some typical problems and corresponding solutions. In general, the current state of such disharmony is mostly caused by the failure of the system platform to fully consider the appeal of developers and the poor compatibility of component design with historical business. Of course, the present state is certainly not a reasonable state. In the future, whether it is the system platform side, the business side, or the developer, when conflicts cannot be coordinated, there will always be a party to compromise. Before this time point comes, I hope the above summary can provide some help for students who suffer from such problems.