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

This example project uses UIKitand WKWebviewand is only supported on iOS.

Download the Swift example code from GitHub.

Run the example project

  1. On your computer, open the Xcode project provided in the link above.

  2. Under Signing and Capabilities, set Provisioning profile and Signing Certificate as shown below.

  3. 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 the event.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

Change request #130: Asset API