Subchapter 4.4
references/third-party.mdMarkdown5 KBView on GitHub
This reference is for package authors, not app developers. It describes how a library ships an optional EAS Observe integration so that apps using the library get events about problems that application code cannot detect on its own.
Source: https://docs.expo.dev/eas/observe/integrations/third-party/ (opens in a new tab) — consult this page for the latest guidance.
App-side setup lives in ./setup.md. Querying the resulting events is in ./queries.md.
Report actionable issues the developer can fix. Good candidates:
Do not use it for general product analytics or for anything the app author could log themselves.
Observe.registerIntegration() is not available earlier.expo-observe installed and a build produced.The package must keep working when expo-observe is absent. Declare it as an optional peer dependency plus a dev dependency for types and tests. Never make it a required runtime dependency.
{
"peerDependencies": { "expo-observe": ">=57.0.0" },
"peerDependenciesMeta": { "expo-observe": { "optional": true } },
"devDependencies": { "expo-observe": "^57.0.0" }
}Load it with require() inside try/catch, and type it with typeof import() so the types survive:
// observe.ts
let observeModule: typeof import('expo-observe') | undefined;
try {
observeModule = require('expo-observe') as typeof import('expo-observe');
} catch {
// The integration stays disabled when expo-observe is not installed.
}Use declaration merging to add your integration key to ObserveIntegrationsConfig:
// observe.types.ts
export type YourPackageIntegrationConfig = {
thresholdMs?: number;
};
declare module 'expo-observe' {
interface ObserveIntegrationsConfig {
'your-package'?: boolean | YourPackageIntegrationConfig;
}
}Export this declaration from the package entry point so TypeScript loads it when the app imports your package. App developers then enable it the same way they enable the first-party integrations:
Observe.configure({
integrations: {
'your-package': true,
// or, with options:
// 'your-package': { thresholdMs: 1500 },
},
});Observe.registerIntegration(name, callback) invokes the callback once, when the named integration config becomes available. The callback does not run when the key is omitted or set to false.
// observe.ts
export function initObserveIntegration() {
// The `typeof window` check skips initialization during server-side rendering on web.
if (typeof window !== 'undefined' && observeModule) {
const { Observe } = observeModule;
Observe.registerIntegration('your-package', config => {
if (config) {
enableObserveIntegration(config === true ? {} : config);
}
});
}
}Call the initializer from the package entry point:
// index.ts
import { initObserveIntegration } from './observe';
export type { YourPackageIntegrationConfig } from './observe.types';
initObserveIntegration();Note the config === true normalization: the key accepts either a boolean or an options object, so collapse true to {} before using it.
Emit through Observe.logEvent() when the package detects an actionable issue. Guard on both module presence and enablement, so a disabled integration costs nothing.
export function logExpensiveOperation(durationMs: number, thresholdMs: number) {
if (!observeModule || !enabled) {
return;
}
const { Observe } = observeModule;
Observe.logEvent('your-package.expensive-operation', {
severity: 'warn',
body: 'Reduce the work performed by this operation or increase the configured threshold.',
attributes: { durationMs, thresholdMs },
});
}Naming rules, matching the app-side event conventions in ./setup.md:
your-package.expensive-operation.severity to separate warnings from errors, and body for the remediation hint. The attributes carry the measurements.