Advanced scheme resolution and validation for Expo and React Native applications, handling development vs production environments and app configuration.
Determines if the app uses a custom URL scheme based on the execution environment.
/**
* Check if app uses custom URL scheme based on execution environment
* - Bare workflow: always uses custom scheme
* - Standalone: uses custom scheme when defined in manifest
* - Store client (Expo Go): uses default scheme
* @returns true if app uses custom scheme, false otherwise
*/
function hasCustomScheme(): boolean;Usage Example:
import { hasCustomScheme } from "expo-linking";
if (hasCustomScheme()) {
console.log("App uses custom scheme - deep links will use app-specific protocol");
} else {
console.log("App uses default scheme - likely running in Expo Go");
}Retrieves platform-specific schemes from the Expo configuration manifest.
/**
* Collect a list of platform schemes from the manifest.
* Based on the Scheme modules from @expo/config-plugins used for collecting schemes before prebuilding.
* Resolution order:
* - Android: scheme -> android.scheme -> android.package
* - iOS: scheme -> ios.scheme -> ios.bundleIdentifier
* @returns Array of scheme strings defined in the app configuration
*/
function collectManifestSchemes(): string[];Usage Example:
import { collectManifestSchemes } from "expo-linking";
const schemes = collectManifestSchemes();
console.log("Available schemes:", schemes);
// Example output: ["myapp", "myapp-dev", "com.company.myapp"]
// Check if specific scheme is available
const hasDevScheme = schemes.includes("myapp-dev");
if (hasDevScheme) {
console.log("Development scheme is configured");
}Verifies that the expo-constants manifest is properly linked in bare workflow.
/**
* Ensure the user has linked the expo-constants manifest in bare workflow.
* Required for scheme resolution and deep linking functionality.
* @returns true if constants manifest is available, false otherwise
*/
function hasConstantsManifest(): boolean;Usage Example:
import { hasConstantsManifest } from "expo-linking";
if (!hasConstantsManifest()) {
console.warn("expo-constants manifest not found - deep linking may not work properly");
console.warn("Follow setup instructions: https://github.com/expo/expo/blob/main/packages/expo-constants/README.md");
}Resolves the appropriate scheme for deep linking with validation and environment handling.
/**
* Resolve appropriate scheme for deep linking, handles validation and warnings.
* Throws errors in production if no scheme is configured.
* @param options - Configuration options for scheme resolution
* @returns The resolved scheme string
* @throws Error if no scheme is configured in production builds
*/
function resolveScheme(options: ResolveSchemeOptions): string;
interface ResolveSchemeOptions {
/** Preferred scheme to use - will be validated against app config */
scheme?: string;
/** Whether to suppress validation warnings in development */
isSilent?: boolean;
}Usage Examples:
import { resolveScheme } from "expo-linking";
// Use default scheme from app config
const defaultScheme = resolveScheme({});
console.log("Default scheme:", defaultScheme);
// Use specific scheme with validation
const customScheme = resolveScheme({
scheme: "myapp-production"
});
console.log("Custom scheme:", customScheme);
// Silent resolution (no warnings)
const silentScheme = resolveScheme({
scheme: "myapp-dev",
isSilent: true
});Environment-Specific Behavior:
'exp' or provided scheme if it's a valid Expo client schemeimport {
resolveScheme,
collectManifestSchemes,
hasCustomScheme
} from "expo-linking";
function getOptimalScheme(environment: 'dev' | 'staging' | 'prod'): string {
const availableSchemes = collectManifestSchemes();
// Select scheme based on environment
const envSchemeMap = {
dev: `myapp-dev`,
staging: `myapp-staging`,
prod: `myapp`
};
const preferredScheme = envSchemeMap[environment];
if (availableSchemes.includes(preferredScheme)) {
return resolveScheme({ scheme: preferredScheme });
}
// Fallback to default
return resolveScheme({});
}import {
collectManifestSchemes,
hasCustomScheme,
hasConstantsManifest
} from "expo-linking";
interface SchemeValidationResult {
isValid: boolean;
hasCustomScheme: boolean;
availableSchemes: string[];
warnings: string[];
errors: string[];
}
function validateSchemeSetup(): SchemeValidationResult {
const warnings: string[] = [];
const errors: string[] = [];
// Check if constants manifest is available
if (!hasConstantsManifest()) {
errors.push("expo-constants manifest not found");
}
const schemes = collectManifestSchemes();
const customScheme = hasCustomScheme();
if (!customScheme && schemes.length === 0) {
warnings.push("No custom scheme configured - app may not handle deep links properly in production");
}
if (schemes.length > 1) {
warnings.push(`Multiple schemes found: ${schemes.join(', ')} - first scheme will be used by default`);
}
return {
isValid: errors.length === 0,
hasCustomScheme: customScheme,
availableSchemes: schemes,
warnings,
errors
};
}
// Usage
const validation = validateSchemeSetup();
if (!validation.isValid) {
console.error("Scheme setup errors:", validation.errors);
}
if (validation.warnings.length > 0) {
console.warn("Scheme setup warnings:", validation.warnings);
}{
"expo": {
"scheme": "myapp",
"ios": {
"bundleIdentifier": "com.company.myapp",
"scheme": "myapp-ios"
},
"android": {
"package": "com.company.myapp",
"scheme": "myapp-android"
}
}
}{
"expo": {
"scheme": ["myapp", "myapp-dev", "myapp-staging"]
}
}