Riddle on iOS and macOS

You can use Riddle in your iOS / macOS app. We have tested with SwiftUI, iOS 16.4, macOS 13.3, and Xcode 13.0, but it should also work with older versions and UIKit.

There are two ways to use Riddle in your app:

  • Riddle Showcase in a WebView in your app
  • Your own website in a WebView in your app

Riddle Showcase in a WebView in your app

In this example, the Riddle Showcase URL will open in a WebView in your app.

What you need for this:

The Riddle Showcase URL

The Riddle Showcase URL is made up of two parts:

To find your Riddle ID:

  1. In your Riddle, go to PUBLISH.
  2. Click on Embed / Showcase.
  3. Click on GET THE CODE.
    In this code you will find the Riddle Showcase URL.
  4. Copy the Riddle Showcase URL.
  5. Enter the Riddle Showcase URL in this line:
let url = URL(string: "https://www.riddle.com/embed/a/RIDDLEID")!

showcase url

Riddle in your own web page in a WebView in your app

In this example, your own web page is opened in a WebView in your app. You can then embed Riddle in this webpage.

What you need for this:

  • A WebView in your app
  • Your own website with embedded Riddle
  • Your domain must be activated for Riddle

Your own website with embedded Riddle

You can embed Riddle in your own website. It is important that you use the normal embed code.

Your domain must be activated for Riddle

For Riddle to work on your domain, it must be activated on Riddle. You can find out how this works here.

Enter your own URL in this line:

let url = URL(string: "https://www.example.com")!

JavaScript to communicate with your app

In order for your app to communicate with Riddle, you need to run JavaScript in your WebView. This JavaScript then sends data to your app.

Riddle sends JavaScript postMessages when the status of the Riddle changes. You can receive these postMessages in your app and then respond accordingly.

Running JavaScript in your WebView

In order to run JavaScript in your WebView, you need to add a WKUserScript in your WebView. This WKUserScript is then executed when the WebView is loaded.

let userScript = WKUserScript(source: javascript,
                                injectionTime: .atDocumentEnd,
                                forMainFrameOnly: false)

let webView = WKWebView()
webView.navigationDelegate = context.coordinator
webView.configuration.userContentController.add(context.coordinator, name: "postMessageHandler")
webView.configuration.userContentController.addUserScript(userScript)
webView.load(request)

The JavsScript is passed as a string. This string can be in your app or you can load it from a file. In this example, an event handler is added that responds to postMessages and sends them to your app as a JSON string. In addition, a postMessage is sent to Riddle so that a variable is stored in the Riddle data layer.

let javascript = """
// Hier dein JavaScript-Code

window.postMessage({
    dataLayer: [{ key: 'username', value: 'Tom Riddle' }],
}, "*");

window.addEventListener("message", (event) => {
    window.webkit.messageHandlers.postMessageHandler.postMessage(JSON.stringify(event.data));
});
"""

Alternatively, you can insert the JavaScript into your own web page. Then you don't have to include it in your app.

The handleEvent function is called when a postMessage is received. In this function you can react accordingly. For example, you can store whether the user has completed the Riddle or not and give a reward accordingly.

func handleEvent(jsonMsg: String) {
    print("Received Post-Message: \(jsonMsg)")
}

Example Code in SwiftUI

import SwiftUI
import WebKit

func handleEvent(jsonMsg: String) {
    print("Received Post-Message: \(jsonMsg)")
}

#if os(macOS)
struct WebView: NSViewRepresentable {
    let request: URLRequest
    let javascript: String

    func makeNSView(context: Context) -> WKWebView {
        let userScript = WKUserScript(source: javascript,
                                      injectionTime: .atDocumentEnd,
                                      forMainFrameOnly: false)
        
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        webView.configuration.userContentController.add(context.coordinator, name: "postMessageHandler")
        webView.configuration.userContentController.addUserScript(userScript)
        webView.load(request)
        return webView
    }

    func updateNSView(_ nsView: WKWebView, context: Context) {
        nsView.evaluateJavaScript(javascript, completionHandler: nil)
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            if let body = message.body as? String {
                handleEvent(jsonMsg: body)
            }
        }
    }
}

#else
struct WebView: UIViewRepresentable {
    let request: URLRequest
    let javascript: String

    func makeUIView(context: Context) -> WKWebView {
        // Create user script that inject the JavaScript after the HTML finishes loading
        let userScript = WKUserScript(source: javascript,
                                      injectionTime: .atDocumentEnd,
                                      forMainFrameOnly: false)

        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        webView.configuration.userContentController.add(context.coordinator, name: "postMessageHandler")
        webView.configuration.userContentController.addUserScript(userScript)
        webView.load(request)
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {
        webView.evaluateJavaScript(javascript, completionHandler: nil)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            if let body = message.body as? String {
                handleEvent(jsonMsg: body)
            }
        }
    }
}
#endif

struct ContentView: View {
    let url = URL(string: "https://www.riddle.com/embed/a/RIDDLEID")!
    let javascript = """
        // Hier dein JavaScript-Code

        window.postMessage({
            dataLayer: [{ key: 'username', value: 'Tom Riddle' }],
        }, "*");

        window.addEventListener("message", (event) => {
            window.webkit.messageHandlers.postMessageHandler.postMessage(JSON.stringify(event.data));
        });
        """

    var body: some View {
        Text("Example App")
        
        WebView(request: URLRequest(url: url), javascript: javascript)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}