Skip to content

Instantly share code, notes, and snippets.

@wplong11
Created August 30, 2022 12:27
Show Gist options
  • Save wplong11/ddecf484bf275136ea410b69acbc80a2 to your computer and use it in GitHub Desktop.
Save wplong11/ddecf484bf275136ea410b69acbc80a2 to your computer and use it in GitHub Desktop.
NativeMethodInvoker.ts
type NativeMethodRequest<TParams> = {
invocationId: string;
methodName: string;
params?: TParams;
};
type NativeMethodResponse = {
invocationId: string;
callbackType: 'resolve' | 'reject';
value?: any;
};
const handler = "__NativeMethodInvoker__"
export class NativeMethodInvoker {
private callbacks: {
[invocationId: string]: {
resolve: (value: any) => void;
reject: (reason?: any) => void;
};
} = {};
constructor() {
window.addEventListener('message', this.handleWindowMessageEvent);
}
public close() {
window.removeEventListener('message', this.handleWindowMessageEvent);
this.callbacks = {};
}
public invoke<TParams, TReturn>(methodName: string, params: TParams): Promise<TReturn> {
const invocationId = this.generateUUID();
const promise = new Promise<TReturn>((resolve, reject) => {
this.callbacks[invocationId] = { resolve, reject };
});
this.post({
invocationId,
methodName,
params,
});
return promise;
}
private generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const random = (Math.random() * 16) | 0;
const value = c === 'x' ? random : (random & 0x3) | 0x8;
return value.toString(16);
});
}
private handleWindowMessageEvent = (event: MessageEvent) => {
if (event.origin !== window.location.origin) return;
const response: NativeMethodResponse = event.data;
if (!response.invocationId) return;
if (!response.callbackType) return;
const callback = this.callbacks[response.invocationId];
delete this.callbacks[response.invocationId];
callback[response.callbackType](response.value);
};
private post<TParams>(request: NativeMethodRequest<TParams>) {
try {
const w: any = window;
if (w[handler] && w[handler].handle) {
w[handler].handle(JSON.stringify(request));
} else if (w.webkit && w.webkit.messageHandlers[handler]) {
w.webkit.messageHandlers[handler].postMessage(request);
}
} catch (err) {
console.error(`Fail to post request\n`, err);
}
}
}
@wplong11
Copy link
Author

wplong11 commented Aug 30, 2022

Sample.tsx

import React from 'react';
import './App.css';
import { NativeMethodInvoker } from './NativeMethodInvoker';

const nativeMethodInvoker = new NativeMethodInvoker();

async function getHelloMessage(firstValue: string, secondValue: number): Promise<string> {
  return nativeMethodInvoker.invoke("getHelloMessage", {
    firstValue,
    secondValue,
  });
}
async function getDeviceId(): Promise<string> {
  return nativeMethodInvoker.invoke("DeviceIdReader.getDeviceId", {});
}
async function getAuthorizationStatus(): Promise<"notDetermined" | "authorized" | "denied" | "unknown"> {
  return nativeMethodInvoker.invoke("PushNotificationService.getAuthorizationStatus", {});
}
async function requestAuthorization(): Promise<"notDetermined" | "authorized" | "denied" | "unknown"> {
  return nativeMethodInvoker.invoke("PushNotificationService.requestAuthorization", {});
}
async function getClipboardString(): Promise<string | null> {
  return nativeMethodInvoker.invoke("Clipboard.getString", {});
}

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <button onClick={async () => alert(await getClipboardString())}>클립보드 조회</button>
        <button onClick={async () => alert(await getHelloMessage("1", 2))}>Hello 메시지 조회</button>
        <button onClick={async () => alert(await getDeviceId())}>DeviceId 조회</button>
        <button onClick={async () => alert(await getAuthorizationStatus())}>PushNotification 권한 조회</button>
        <button onClick={async () => alert(await requestAuthorization())}>PushNotification 권한 요청</button>
      </header>
    </div>
  );
}

export default App;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment