-
Notifications
You must be signed in to change notification settings - Fork 0
Exposing TypeScript functions to the browser
Justin Bush edited this page Jun 5, 2024
·
9 revisions
The goal of this article is to explore the possible options for exposing functions, variables and WebKit message handlers to the browser in the simplest and quickest way possible.
In order to explore these options fully, let's take a look at what a vanilla implementation might look like:
Define types to be exposed:
declare global {
interface Window {
webkit: {
messageHandlers: {
// Example 1: Send message to Swift app
someMessageHandler: {
someStringFunction: (someParam: string) => void;
};
};
};
// Example 2: Receive message from Swift app
sendMessageToReactWebApp: (message: string) => void;
}
}
Then, define the function body for each:
Example 1
const sendMessageToSwiftApp = (message: string) => {
if (
window.webkit &&
window.webkit.messageHandlers &&
window.webkit.messageHandlers.someMessageHandler
) {
window.webkit.messageHandlers.someMessageHandler.sendStringToSwiftApp(
message
);
} else {
console.warn("Message handler 'someMessageHandler' is not available.");
}
};
sendMessageToSwiftApp('Hello from React!')
Example 2
const sendMessageToReactWebApp = useCallback((message: string) => {
console.log(`React received message from Swift app: ${message}`);
sendMessageToSwiftApp(`Message received from React app: ${message}`);
}, []);
useEffect(() => {
window.sendMessageToReactWebApp = sendMessageToReactWebApp;
}, [sendMessageToReactWebApp]);
That's a lot of code. It sure would be nice if we could simplify a lot of this. Let's see what our options are...
We'll start with exposing functions.
declare global {
interface Window {
sendMessageToReactWebApp: (message: string) => void;
}
}
const Main = () => {
const sendMessageToReactWebApp = useCallback((message: string) => {
console.log(`React received message from Swift app: ${message}`);
sendMessageToSwiftApp(`Message received from React app: ${message}`);
}, []);
useEffect(() => {
window.sendMessageToReactWebApp = sendMessageToReactWebApp;
}, [sendMessageToReactWebApp]);
}
useExposeFunction.ts
import { useEffect, useCallback } from 'react';
const useExpose = <T extends (...args: any[]) => void>(func: T) => {
const memoizedFunc = useCallback(func, [func]);
useEffect(() => {
const functionName = func.name as keyof Window;
(window as any)[functionName] = memoizedFunc;
return () => {
delete (window as any)[functionName];
};
}, [memoizedFunc, func.name]);
};
export default useExpose;
declare global {
interface Window {
sendMessageToReactWebApp: (message: string) => void;
}
}
const Main = () => {
const sendMessageToReactWebApp = (message: string) => {
console.log(`React received message from Swift app: ${message}`);
sendMessageToSwiftApp(`Message received from React app: ${message}`);
};
useExpose(sendMessageToReactWebApp);
}
Note: Options are still being investigated.
declare global {
interface Window {
webkit: {
someMessageHandler: {
sendStringToSwiftApp: (stringParam: string) => void;
};
};
}
}
const Main = () => {
const sendMessageToSwiftApp = (message: string) => {
if (
window.webkit &&
window.webkit.messageHandlers &&
window.webkit.messageHandlers.someMessageHandler
) {
window.webkit.messageHandlers.someMessageHandler.sendStringToSwiftApp(message);
} else {
console.warn("Message handler 'someMessageHandler' is not available.");
}
}
src/types/global.d.ts
export {};
export type AllHandlers = SomeHandlers & MoreHandlers;
declare global {
interface Window {
webkit: {
messageHandlers: AllHandlers;
};
}
}
src/types/someHandlers.d.ts
export interface SomeHandlers {
someMessageHandler: {
sendStringToSwiftApp: (stringParam: string) => void;
};
// ...
}
src/types/moreHandlers.d.ts
export interface MoreHandlers {
anotherMessageHandler: {
anotherFunction: (boolParam: boolean) => void;
};
// ...
}
src/app/utils/handlers.ts
import { AllHandlers } from '../../types/global';
const isBrowser = typeof window !== 'undefined';
const getHandlers = (): AllHandlers => {
if (isBrowser && window.webkit && window.webkit.messageHandlers) {
return window.webkit.messageHandlers as AllHandlers;
}
return {} as AllHandlers;
};
export const handlers: AllHandlers = getHandlers();
const sendMessageToSwiftApp = (message: string) => {
handlers.someMessageHandler.sendStringToSwiftApp(message);
}