At WWDC 2018, Apple announced it was deprecating its original webview framework (UIWebView) in favor of a WebKit-based framework. WebKit is an open-source project used by both browsers and webviews, including Safari. The overall architecture of WKWebView is quite different than UIWebView, and this dramatically impacts what we can do when debugging hybrid apps, such as our own Salesforce App.
The Salesforce App is full of powerful native features, but the incredible flexibility of the app is due to its Hybrid nature. Hybrid apps allow for the benefits of native apps while using WebViews to deliver experiences built on web technologies. In the case of the Salesforce App, this includes Visualforce, Lightning (Aura), and Lightning Web Components.
The first major shift from UIWebView to WebKit was moving from an in-process framework to a multi-process architecture. WebKit splits functionality into multiple processes, multiple copies of which may be running at a single time.
- UIProcess — This is your app. The WKWebview code and WebKit framework is loaded in your process space
- WebContent, aka WebProcess — This is where JS and DOM memory allocations reside
- Network Process — This process is responsible for making the underlying network requests associated with web requests
- Storage Process — This process is used for database and service worker storage.
Because WebKit now splits responsibility across multiple processes, not everything occurs in your own process space. As a consequence, debugging hybrid issues may require attaching to multiple processes. Unfortunately, attaching a debugger to a process requires that the process is built with specific entitlements, which is not the case for the WebKit processes deployed on iOS devices or in the Simulator runtime environments. But, there IS a way!
Get the WebKit Source
The first step for debugging hybrid app behavior under WebKit is to get the WebKit source code. The WebKit source is quite large; at the time of this article, it takes up 10.4Gb of storage.
You can pull the source using Git or SVN. Although the WebKit team prefers SVN, we prefer Git, so we will write from that perspective. Clone the WebKit repo as follows:
git clone git://git.webkit.org/WebKit.git WebKit
The Git repo does not track all of the branches that the SVN repository does. For most debugging, you can use master; however, if you are trying to troubleshoot a specific behavior tied to a particular iOS release, you will have more work to do. The iOS release branches are not present in the Git repo, so you will have to set up tracking of the SVN branches from Git. If this is a requirement, follow the steps at the WebKit blog here. This will use considerably more space, so be prepared. Also, expect some challenges getting git-svn up and working on Catalina, so only attempt this if necessary.
Build WebKit for the iOS Simulator
I find that staying on the latest GA version of Xcode will minimize any compilation issues you have with WebKit source, but your mileage may vary. Keep in mind that you can have multiple versions of Xcode installed and can build WebKit with one version and your app in another.
The WebKit team has included some Perl5 and Python3 scripts which automate much of the work. If any one of these scripts fails, a first step might be to ensure you are running a current version of Python3 and Perl5.
The first thing we do is prepare our Xcode environment for building WebKit using the script:
This script will copy a few files from the Mac OSX SDK to the iOS SDK folders, generate a stub header, and other goodies that are required before we can build and run WebKit on iOS.
Next, we build the frameworks using another script provided by the WebKit team:
You can run build-webkit without any params to see additional options, including
--debug. This will build and place the WebKit frameworks in the WebKitBuild subdirectory of where you cloned the Git repo. Go get something to drink, read a book, watch a movie; this will take a while.
Using development build WebKit with hybrid apps
Now we have some fancy new frameworks and xpc files in our WebKitBuild folder, but that does us no good if we cannot get the iOS simulator to use these frameworks instead of the standard WebKit frameworks. This can be done in one of two ways: via the command-line using the WebKit team’s scripts, or through Xcode.
Command Line — Run-WebKit-App
This method relies on another Perl Script provided by the WebKit team. To use it, you must build your app bundle and know the path to it. Typically your app will get built under derived data.
Launch your app using the following command:
Tools/Scripts/run-webkit-app <Path to your .App File> --ios-simulator
- Creates a new simulator image
- Installs your app bundle
- Executes your app in a runtime environment, which uses the previously built WebKit frameworks instead of the standard WebKit frameworks.
Once your app is up and running, you can use Xcode to attach to your process and the WebKit processes. The WebKit processes built locally will have the suffix “.Development,” for example, com.apple.WebKit.WebContent.Development.
Another method for launching your app with debug versions of WebKit is from the WebKit Xcode project. This approach has an advantage that the debugger will attach to all WebKit spawned processes. The command line method will launch the app in the simulator, but the debugger must be attached independently. If WebKit has spawned multiple processes, it may be challenging to find the specific one you want to attach. Also, if you’re going to break at a very early point in the lifecycle of WebKit, you may not be able to accurately time the attach operation.
Open the WebKit workspace in Xcode, and then under File → Workspace Settings, select the following:
- Change the Derived Data selection to “Workspace-relative Location”.
- Type WebKitBuild in the text box.
- Click Advanced
- Select Custom
- Change the drop down list to Relative to Workspace.
- Type WebKitBuild in the text boxes for Products and Intermediates.
The next step is to point Xcode to our app.
- Select WebKit as the active scheme under the Product → Scheme
- Select Product → Scheme → Edit Scheme
- Select the Run action, and you will see that there is no executable defined when we run WebKit
- In the drop-down list for Executable, select Other and then locate the. App file for your app.
Now we are good to go!
- Select Product → Perform Action → Run Without Building to launch your app with the local versions of WebKit.
- You will see the debugger auto-attach not only to your app, but also to the WebKit processes.
Hopefully this article lets you explore a bit deeper into your Hybrid stack and better understand how the WebKit layer on which you rely works. You may now debug into WebKit source code, or run Instruments to profile the WebKit processes when running your web apps. The WebKit team frequently posts helpful information to their blog at https://webkit.org/blog/, which is where much of this information came from.