Swift example project
Integrate the Ready Player Me Avatar Creator into a native iOS application using WKWebview.
The following example Swift project shows how the Ready Player Me Avatar Creator can be integrated into a native iOS application using WKWebview
.
Get the code
Download the Swift example code from GitHub.
Run the example project
On your computer, open the Xcode project provided in the link above.
Under Signing and Capabilities, set Provisioning profile and Signing Certificate as shown below.
Build and run the project on your target iOS device. You should be able to see and use the Avatar Creator from your app.

How it works
The code for integrating the Avatar Creator is in three .swift files that need to be part of your own project as well.
WebViewController.swift
WebViewController
is a custom UIViewController
for handling displaying the WebView browser. It uses WebKit
(WKWebView
) and UIKit
.
This section looks at Ready Player Me specific code in this file.
Ready Player Me website Url
If you have a Ready Player Me subdomain, you can edit the URL for the Avatar Creator to point to your own subdomain by setting the subdomain
at the top of the file to your own.
let subdomain = "demo" ==> let subdomain = "yoursubdomain"
Javascript injection
When the user clicks the Next button in the Avatar Creator, their avatar asset is baked and a URL for the resulting .glb file is displayed.
This triggers a Javascript event with a message string that contains this generated avatar URL.
You can retrieve this URL with an eventlistener
.
Inside the WebViewController
, find the variable source
. It holds the code to
add an event listener listening for a
message
,subscribes to events from Ready Player Me,
and handles the messages with a callback to native Swift using
webkit.messageHandlers
. This callback passes theevent.data
(which contains the Ready Player Me avatar's .glb file URL).
let source = """
window.addEventListener('message', function(event){
const json = parse(event)
if (json?.source !== 'readyplayerme') {
return;
}
// Susbcribe to all events sent from Ready Player Me once frame is ready
if (json.eventName === 'v1.frame.ready') {
window.postMessage(
JSON.stringify({
target: 'readyplayerme',
type: 'subscribe',
eventName: 'v1.**'
}),
'*'
);
}
window.webkit.messageHandlers.iosListener.postMessage(event.data);
function parse(event) {
try {
return JSON.parse(event.data)
} catch (error) {
return null
}
};
});
"""
Setting up the WebView
The loadView()
function is responsible for initializing the WebView, injecting the Javascript snippet, and linking the callback function mentioned above.
First, create a WKWebViewConfiguration
object and a WKUserScript
, which are used to create a bridge between the WKWebView's browser, Javascript, and the Native code. The source:
parameter is set to the source
variable defined above that contains the JavaScript snippet.
let config = WKWebViewConfiguration()
let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
Next, add the WKUserScript
to the config
object.
config.userContentController.addUserScript(script)
Set the delegate function by passing a WKScriptMessageHandler
of self
and the name to use for the callback function.
config.userContentController.add(self, name: "iosListener")
Because the WebViewController
inherits from WKScriptMessageHandler
, you can pass self
as the first parameter. The name passed for the callback function is iosListener
, which is the same as the function called in the above Javascript snippet. These must match for the bridge to work.
Finally, initialize the WKWebView
, passing the frame bounds and the config
object, and assign this view as the WebView
to ensure it is then displayed.
webView = WKWebView(frame: .zero, configuration: config)view = webView
The next important function is userContentController()
. This function is called as the callback function in response to the Javascript event. Here, you have access to the data from the Javascript event using message.body
. In this example, you pass this to a avatarurlDelegate.avatarUrlCallback
function that passes the data to the main ViewController which is then displayed in a native popup.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
avatarUrlDelegate?.avatarUrlCallback(url : "\(message.body)")
}
ViewController.swift
The ViewController.swift
file implements the main view controller.
In this example, it contains functionality for spawning, displaying, and retrieving the information (Ready Player Me avatar URL) from the WebViewController
.
The viewDidLoad()
function calls the createWebView()
function, sets the visibility of the buttons to true
, and hides the webViewController
.
The result of the hasCookies()
function determines whether to hide the editAvatarButton
. It is only possible to edit an existing avatar if one has been created, and the avatar data is stored in the browser's cookies.
override func viewDidLoad() {
super.viewDidLoad()
createWebView()
editAvatarButton.isHidden = true
webViewController.view.isHidden = true
editAvatarButton.isHidden = !webViewController.hasCookies()
}
Creating the WebViewController
The createWebView()
function creates and configures the WebViewController
.
With webViewController.avatarUrlDelegate = self
, it assigns itself as an avatarUrlDelegate
. This is important for receiving the data from the Javascript event in the WebViewController
.
This function also sets the size of the window and the tag
used as an identifier inside the destroyWebView()
function.
func createWebView(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: webViewIdentifier) as UIViewController
guard let viewController = controller as? WebViewController else {
return
}
webViewController = viewController
webViewController.avatarUrlDelegate = self
addChild(controller)
self.view.addSubview(controller.view)
controller.view.frame = view.safeAreaLayoutGuide.layoutFrame
controller.view.tag = webViewControllerTag
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controller.didMove(toParent: self)
}
Receiving the avatar URL
The avatarUrlCallback(url: string)
function is called by the webViewController
when the avatar creation process has completed. It displays an alert with the URL and hides the webViewController
and editAvatarButton
.
func avatarUrlCallback(url: String){
showAlert(message: url)
webViewController.view.isHidden = true
editAvatarButton?.isHidden = false
}
Button action functions
Both button functions are similar, control the visibility of the WebViewController,
and force a page reload.
However, the onCreateNewAvatarAction
function must remove all previous avatar data. It destroys and recreates the web view, reloads the page, and clears the history with webViewController.reloadPage(clearHistory: true)
.
@IBAction func onCreateNewAvatarAction(_ sender: Any) {
destroyWebView()
createWebView()
webViewController.reloadPage(clearHistory: true)
webViewController.view.isHidden = false
}
@IBAction func onEditAvatarAction(_ sender: Any) {
webViewController.view.isHidden = false
webViewController.reloadPage(clearHistory: false)
}
Finally, the destroyWebView()
function uses the view.tag
identifier set in the CreateWebView()
function to determine which view to remove.
func destroyWebView(){
if let viewWithTag = self.view.viewWithTag(webViewControllerTag) {
webViewController.dismiss(animated: true, completion: nil)
viewWithTag.removeFromSuperview()
}else{
print("No WebView to destroy!")
}
WebCacheCleaner.swift
This is a static utility class webCacheCleaner.swift
with a single function clean()
for clearing the web view browser's cookies and cache.
Last updated
Was this helpful?