Usage
Deep Link Services allows handling custom URL schemes and universal links on mobile devices.
Essential Kit wraps native iOS and Android deep link APIs into a single Unity interface. When users click deep links, Essential Kit automatically routes them to your app and fires events with the link data.
Table of Contents
Deep Link Flow at a Glance
[Player taps deep link]
↓
[iOS / Android resolves scheme or domain]
↓
[Essential Kit DeepLinkServices]
(queues event until handlers subscribe)
↓
[OnCustomSchemeUrlOpen or OnUniversalLinkOpen]
↓
[Your routing code → load content, grant rewards, etc.]Understanding Deep Link Anatomy
Deep links contain three main parts that you'll use for routing:
Example 1: mygame://invite/player123
Scheme:
mygame- Identifies your appHost:
invite- Identifies the actionPath:
/player123- Contains action parameters
Example 2: https://yourgame.com/level/5?mode=hard
Scheme:
https- Universal link schemeHost:
yourgame.com- Your domainPath:
/level/5- Content to displayQuery:
?mode=hard- Additional parameters
You'll use these components to determine what content to show when a deep link opens your app.
Import Namespaces
using System;
using VoxelBusters.EssentialKit;
using VoxelBusters.CoreLibrary;
using System.Collections.Generic;Event Registration
Register for deep link events in OnEnable and unregister in OnDisable:
void OnEnable()
{
DeepLinkServices.OnCustomSchemeUrlOpen += OnCustomSchemeUrlOpen;
DeepLinkServices.OnUniversalLinkOpen += OnUniversalLinkOpen;
}
void OnDisable()
{
DeepLinkServices.OnCustomSchemeUrlOpen -= OnCustomSchemeUrlOpen;
DeepLinkServices.OnUniversalLinkOpen -= OnUniversalLinkOpen;
}OnCustomSchemeUrlOpen
When user opens a custom scheme URL (e.g., mygame://)
OnUniversalLinkOpen
When user opens a universal link (e.g., https://yourgame.com/)
Register events early in your app initialization (like in a persistent manager's OnEnable) to ensure no deep links are missed.
Check Availability
Guard your handlers in case the feature is disabled on the current platform:
if (!DeepLinkServices.IsAvailable())
{
Debug.LogWarning("Deep Link Services is not available on this platform or build.");
return;
}The call returns false in editor play mode if you excluded Deep Link Services from the build configuration.
How Deep Links Work
Essential Kit handles deep links intelligently based on your app state:
App Already Running: Events fire immediately when the deep link is activated.
App Launched by Deep Link: Essential Kit delays the event until you register the event handler. This ensures your app is fully initialized before processing the link.
Basic Implementation
Handle both event types with the same logic:
void OnCustomSchemeUrlOpen(DeepLinkServicesDynamicLinkOpenResult result)
{
Debug.Log($"Received custom scheme deep link: {result.RawUrlString}");
HandleDeepLink(result);
}
void OnUniversalLinkOpen(DeepLinkServicesDynamicLinkOpenResult result)
{
Debug.Log($"Received universal link: {result.RawUrlString}");
HandleDeepLink(result);
}
void HandleDeepLink(DeepLinkServicesDynamicLinkOpenResult result)
{
Uri uri = result.Url;
Debug.Log($"Scheme: {uri.Scheme}");
Debug.Log($"Host: {uri.Host}");
Debug.Log($"Path: {uri.AbsolutePath}");
// Route based on host
switch (uri.Host.ToLower())
{
case "invite":
HandleInvite(uri);
break;
case "shop":
HandleShop(uri);
break;
case "level":
HandleLevel(uri);
break;
default:
Debug.LogWarning($"Unknown deep link action: {uri.Host}");
break;
}
}Result Properties
Url
Uri
Parsed URL with easy access to components (Scheme, Host, Path, Query)
RawUrlString
string
Original URL string as received from the system
Parsing Deep Link Parameters
Use C#'s Uri class to extract deep link data:
Path Segments
void HandleLevel(Uri uri)
{
// mygame://level/5 or https://yourgame.com/level/5
string[] segments = uri.Segments; // ["/", "level/", "5"]
if (segments.Length > 2)
{
string levelId = segments[2].TrimEnd('/'); // "5"
Debug.Log($"Loading level: {levelId}");
Debug.Log($"Trigger gameplay load for level {levelId}.");
}
}Query Parameters
void HandleShop(Uri uri)
{
// mygame://shop?item=sword&discount=50
string query = uri.Query; // "?item=sword&discount=50"
Dictionary<string, string> parameters = ParseQuery(query);
parameters.TryGetValue("item", out string itemId); // "sword"
parameters.TryGetValue("discount", out string discount); // "50"
Debug.Log($"Opening shop item: {itemId} with discount: {discount}%");
Debug.Log($"Apply discount {discount}% to item {itemId}.");
}Always validate deep link parameters before using them. Users can manually create deep links with invalid data.
Common Navigation Patterns
Friend Invitations
void HandleInvite(Uri uri)
{
// mygame://invite?referrer=player123&reward=100gems
Dictionary<string, string> parameters = ParseQuery(uri.Query);
parameters.TryGetValue("referrer", out string referrerId);
parameters.TryGetValue("reward", out string rewardType);
if (string.IsNullOrEmpty(referrerId))
{
Debug.LogWarning("Invalid invite link: missing referrer");
return;
}
Debug.Log($"Processing invite from {referrerId} with reward: {rewardType}");
Debug.Log("Grant referral rewards and track attribution.");
}Tournament Entry
void HandleTournament(Uri uri)
{
// mygame://tournament/weekly_pvp?auto_join=true
string[] segments = uri.Segments;
string tournamentId = segments.Length > 1 ? segments[1].TrimEnd('/') : null;
Dictionary<string, string> parameters = ParseQuery(uri.Query);
bool autoJoin = parameters.TryGetValue("auto_join", out string flag) && flag == "true";
if (!string.IsNullOrEmpty(tournamentId))
{
Debug.Log($"Opening tournament: {tournamentId}");
if (autoJoin)
{
Debug.Log("Auto join the tournament.");
}
else
{
Debug.Log("Show tournament detail UI.");
}
}
}Content Sharing
void HandleLevelChallenge(Uri uri)
{
// mygame://level/25?challenge=speedrun&score=1337
string[] segments = uri.Segments;
string levelId = segments.Length > 1 ? segments[1].TrimEnd('/') : null;
Dictionary<string, string> parameters = ParseQuery(uri.Query);
parameters.TryGetValue("challenge", out string challengeType);
parameters.TryGetValue("score", out string targetScore);
Debug.Log($"Loading level {levelId} with challenge: {challengeType}");
Debug.Log($"Target score for challenge: {targetScore}");
}Add a lightweight helper to reuse the parsing logic:
Dictionary<string, string> ParseQuery(string query)
{
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrEmpty(query))
{
return values;
}
foreach (string part in query.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
{
string[] bits = part.Split(new[] { '=' }, 2);
string key = Uri.UnescapeDataString(bits[0]);
string value = (bits.Length > 1) ? Uri.UnescapeDataString(bits[1]) : string.Empty;
values[key] = value;
}
return values;
}Core APIs Reference
DeepLinkServices.OnCustomSchemeUrlOpen
Event fired when custom scheme URL opens app
DeepLinkServicesDynamicLinkOpenResult via callback
DeepLinkServices.OnUniversalLinkOpen
Event fired when universal link opens app
DeepLinkServicesDynamicLinkOpenResult via callback
DeepLinkServices.IsAvailable()
Check if deep link services are available
bool
DeepLinkServices.Delegate
Advanced: Set custom delegate for filtering links
IDeepLinkServicesDelegate
Advanced: Custom Delegate Filtering
Only use custom delegates for advanced scenarios like ignoring deep links during tutorials or onboarding. For normal deep link handling, use the events directly.
Understanding Custom Delegates
Default Behavior: Essential Kit processes all deep links matching your configuration and fires events automatically.
Advanced Usage: Use a custom delegate to filter which deep links your app should handle:
Ignore deep links during tutorial or onboarding
Filter links based on user authentication status
Prevent deep links in specific game states
Implementation
Create a delegate class:
public class GameDeepLinkDelegate : IDeepLinkServicesDelegate
{
public bool CanHandleCustomSchemeUrl(Uri url)
{
// Don't handle deep links during tutorial
if (GameManager.Instance.IsInTutorial)
{
Debug.Log($"Ignoring deep link during tutorial: {url}");
return false;
}
// Don't handle premium links for non-authenticated users
if (url.Host == "premium" && !UserManager.Instance.IsAuthenticated)
{
Debug.Log("Ignoring premium link - user not authenticated");
return false;
}
return true; // Handle all other links
}
public bool CanHandleUniversalLink(Uri url)
{
// Apply same filtering to universal links
return CanHandleCustomSchemeUrl(url);
}
}Set the delegate before registering events:
void Awake()
{
DeepLinkServices.Delegate = new GameDeepLinkDelegate();
}
void OnEnable()
{
DeepLinkServices.OnCustomSchemeUrlOpen += OnCustomSchemeUrlOpen;
DeepLinkServices.OnUniversalLinkOpen += OnUniversalLinkOpen;
}Advanced delegate filtering is for specific scenarios only. For most games, use standard event handling without custom delegates.
Manual Initialization (Advanced)
Essential Kit auto-initializes Deep Link Services. Only use manual initialization for runtime configuration:
void Awake()
{
var iosLinks = LoadIosDefinitionsFromServer(); // DeepLinkDefinition[]
var androidLinks = LoadAndroidDefinitionsFromServer(); // DeepLinkDefinition[]
var settings = new DeepLinkServicesUnitySettings(
iosProperties: new DeepLinkServicesUnitySettings.IosPlatformProperties(
customSchemeUrls: iosLinks),
androidProperties: new DeepLinkServicesUnitySettings.AndroidPlatformProperties(
universalLinks: androidLinks));
DeepLinkServices.Initialize(settings);
// Set delegate if needed
DeepLinkServices.Delegate = new GameDeepLinkDelegate();
}Use Cases for Manual Initialization:
Dynamically registering URL schemes from server configuration
Setting up deep links for white-label apps with different schemes
Loading deep link configurations from remote JSON at runtime
Related Guides
Demo scene:
Assets/Plugins/VoxelBusters/EssentialKit/Examples/Scenes/DeepLinkServicesDemo.unityPair with Notification Services to send push notifications with deep links
Use with Sharing Services to let players share deep links to social media
Combine with Game Services for social feature deep linking
Ready to test? Head to the Testing Guide to validate your implementation.
Last updated
Was this helpful?