Skip to main content

Events

Lil Snack embeds communicate with parent pages via postMessage events. Partners can listen to these events to track game interactions and respond accordingly.

Game Lifecycle Events

These events fire during various stages of game interaction.

lil-snack-game-ready

Fired when a game has loaded and is ready for interaction.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-game-ready"
  • gameType - Type of game (e.g., "prompt", "swap", "path")
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day

lil-snack-game-visit

Fired when a user visits/views a game.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-game-visit"
  • gameType - Type of game
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day

lil-snack-game-start

Fired when a user starts playing a game.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-game-start"
  • gameType - Type of game
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day

lil-snack-game-end

Fired when a user completes a game.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-game-end"
  • gameType - Type of game
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day
  • win - Boolean indicating if the user won the game

Additional Events

lil-snack-view-how-to

Fired when a user views the "How to Play" instructions.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-view-how-to"
  • gameType - Type of game
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day

lil-snack-post-game-replay

Fired when a user chooses to replay a game after completion.

Parameters:

  • createdAt - ISO timestamp of when the event occurred
  • messageType - "lil-snack-post-game-replay"
  • gameType - Type of game
  • gameId - Unique identifier for the game
  • dayId - Unique identifier for the day

Event Example

Below is an example of how to listen to and respond to Lil Snack game events:

useEffect(() => {
const embedDomain = "lilsnack.com";

const handleGameEvents = (event) => {
// Verify the event is from Lil Snack
if (!event.origin.includes(embedDomain)) return;

const { messageType, gameType, gameId, dayId, createdAt } = event.data;

switch (messageType) {
case "lil-snack-game-ready":
console.log("Game ready:", { gameType, gameId, dayId });
break;

case "lil-snack-game-visit":
console.log("Game visited:", { gameType, gameId, dayId });
break;

case "lil-snack-game-start":
console.log("Game started:", { gameType, gameId, dayId });
// Track analytics, update UI, etc.
break;

case "lil-snack-game-end":
console.log("Game ended:", {
gameType,
gameId,
dayId,
win: event.data.win
});
// Update user stats, show rewards, etc.
break;

case "lil-snack-view-how-to":
console.log("User viewed instructions:", { gameType });
break;

case "lil-snack-post-game-replay":
console.log("User replaying game:", { gameType, gameId });
break;
}
};

window.addEventListener("message", handleGameEvents);

// Cleanup listener on component unmount
return () => {
window.removeEventListener("message", handleGameEvents);
};
}, []);

Mobile Implementation Examples

iOS (Swift/SwiftUI)

import SwiftUI
import WebKit

struct LilSnackGameView: UIViewRepresentable {
let partnerId: String
let gameType: String
let onMessageReceived: ((String, [String: Any]) -> Void)?

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

func makeUIView(context: Context) -> WKWebView {
let contentController = WKUserContentController()

// Inject JavaScript to forward postMessage events to Swift
let script = """
window.addEventListener('message', function(event) {
if (event.origin.includes('lilsnack.com')) {
window.webkit.messageHandlers.lilSnackHandler.postMessage(event.data);
}
});
"""

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

contentController.addUserScript(userScript)
contentController.add(context.coordinator, name: "lilSnackHandler")

let config = WKWebViewConfiguration()
config.userContentController = contentController
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = []

return WKWebView(configuration: config)
}

func updateUIView(_ webView: WKWebView, context: Context) {
let urlString = "https://staging.lilsnack.com/embed-game-direct?partner=\(partnerId)&gt=\(gameType)"
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
webView.load(request)
}
}

class Coordinator: NSObject, WKScriptMessageHandler {
let onMessageReceived: ((String, [String: Any]) -> Void)?

init(onMessageReceived: ((String, [String: Any]) -> Void)?) {
self.onMessageReceived = onMessageReceived
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let messageBody = message.body as? [String: Any],
let messageType = messageBody["messageType"] as? String else { return }

onMessageReceived?(messageType, messageBody)
}
}
}

// Usage
LilSnackGameView(
partnerId: "YOUR_PARTNER_ID",
gameType: "prompt",
onMessageReceived: { messageType, data in
switch messageType {
case "lil-snack-game-ready":
print("Game ready")
case "lil-snack-game-start":
print("Game started")
case "lil-snack-game-end":
if let win = data["win"] as? Bool {
print("Game ended - Win: \(win)")
}
case "lil-snack-view-how-to":
print("User viewed instructions")
case "lil-snack-post-game-replay":
print("User replaying game")
default:
break
}
}
)

Android (Kotlin)

import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import org.json.JSONObject

@Composable
fun LilSnackGameView(
partnerId: String,
gameType: String,
onMessageReceived: (String, JSONObject) -> Unit = { _, _ -> }
) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
WebView(context).apply {
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)

// Inject JavaScript to capture postMessage events
val script = """
window.addEventListener('message', function(event) {
if (event.origin.includes('lilsnack.com')) {
AndroidInterface.handleMessage(JSON.stringify(event.data));
}
});
"""
view?.evaluateJavascript(script, null)
}
}

settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
allowFileAccess = true
allowContentAccess = true
mediaPlaybackRequiresUserGesture = false
}

// Add JavaScript interface
addJavascriptInterface(
object {
@JavascriptInterface
fun handleMessage(message: String) {
try {
val json = JSONObject(message)
val messageType = json.getString("messageType")
onMessageReceived(messageType, json)
} catch (e: Exception) {
e.printStackTrace()
}
}
},
"AndroidInterface"
)

val url = "https://staging.lilsnack.com/embed-game-direct?partner=$partnerId&gt=$gameType"
loadUrl(url)
}
}
)
}

// Usage
LilSnackGameView(
partnerId = "YOUR_PARTNER_ID",
gameType = "prompt",
onMessageReceived = { messageType, data ->
when (messageType) {
"lil-snack-game-ready" -> {
// Game loaded
println("Game ready")
}
"lil-snack-game-start" -> {
// Game started
println("Game started")
}
"lil-snack-game-end" -> {
val win = data.getBoolean("win")
println("Game ended - Win: $win")
}
"lil-snack-view-how-to" -> {
println("User viewed instructions")
}
"lil-snack-post-game-replay" -> {
println("User replaying game")
}
}
}
)

React Native

import React from 'react';
import { WebView } from 'react-native-webview';
import { StyleSheet, View } from 'react-native';

const LilSnackGameView = ({ partnerId, gameType, onMessageReceived }) => {
const gameUrl = `https://staging.lilsnack.com/embed-game-direct?partner=${partnerId}&gt=${gameType}`;

// JavaScript to inject into the WebView
const injectedJavaScript = `
window.addEventListener('message', function(event) {
if (event.origin.includes('lilsnack.com')) {
window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
}
});
true; // Required for injection to work
`;

const handleMessage = (event) => {
try {
const data = JSON.parse(event.nativeEvent.data);
if (onMessageReceived) {
onMessageReceived(data.messageType, data);
}
} catch (error) {
console.error('Error parsing message:', error);
}
};

return (
<View style={styles.container}>
<WebView
source={{ uri: gameUrl }}
style={styles.webview}
injectedJavaScript={injectedJavaScript}
onMessage={handleMessage}
javaScriptEnabled={true}
domStorageEnabled={true}
allowsInlineMediaPlayback={true}
mediaPlaybackRequiresUserAction={false}
/>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
},
webview: {
flex: 1,
},
});

// Usage
const App = () => {
return (
<LilSnackGameView
partnerId="YOUR_PARTNER_ID"
gameType="prompt"
onMessageReceived={(messageType, data) => {
switch (messageType) {
case 'lil-snack-game-ready':
console.log('Game ready');
break;
case 'lil-snack-game-start':
console.log('Game started');
break;
case 'lil-snack-game-end':
console.log('Game ended - Win:', data.win);
break;
case 'lil-snack-view-how-to':
console.log('User viewed instructions');
break;
case 'lil-snack-post-game-replay':
console.log('User replaying game');
break;
}
}}
/>
);
};

export default LilSnackGameView;