This example demonstrates how to integrate the SpeedOf.Me speed test API into a React Native app using react-native-webview.
The SpeedOf.Me API is JavaScript-based, so React Native apps use WebView to:
onMessage propnpm install react-native-webview
# For iOS
cd ios && pod install
SomApi.account = "YOUR_API_KEY";
SomApi.domainName = "your-domain.com";
Upload speedtest.html to the domain registered on your account and point the WebView at that URL:
const webViewSource = { uri: 'https://your-domain.com/speedtest.html' };
It has to be a remote http(s) URL. The engine validates the page that embeds api.js against the domain registered on your account, and it reads that from the page's own URL. A page with no host can never match, so every test fails with error 1002 (Domain Mismatch). That rules out an inline html source (which loads as about:blank) and a file:///android_asset/ asset: neither is a usable loading method for this API.
React Native App
└── SpeedTestScreen
└── WebView (react-native-webview)
└── speedtest.html
└── api.js (defines the SomApi global)
▼
window.ReactNativeWebView.postMessage(json)
▼
onMessage={(event) => {...}}
The HTML page sends messages via:
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'completed',
data: result
}));
React Native receives them:
<WebView
onMessage={(event) => {
const message = JSON.parse(event.nativeEvent.data);
// Handle message
}}
/>
speedtest.html normalizes the engine's payloads before forwarding them, so the React Native side always receives numbers. This is what the raw callbacks give you inside the page.
onProgress(progress):
type: "download", "upload" or "latency"pass: pass number, '' during the latency phasepercentDone: 0 to 100currentSpeed: Mbps, only when SomApi.config.progress.verbose is true, '' otherwise and on every latency eventmaxSpeed: Mbps, same rule as currentSpeed, and '' while the running maximum is still 0latency / jitter: ms, only on the final latency event, '' otherwisespeedtest.html sets SomApi.config.progress.verbose = true, which is what fills in currentSpeed. Without it that field is '' on every event.
onTestCompleted(result) always carries every field. A disabled sub-test sends an empty string rather than omitting the key: download, upload, maxDownload, maxUpload, latency, jitter, testServer, ip_address, hostname, userAgent, testDate. upload and maxUpload are '' when uploadTestEnabled is false, latency and jitter are '' when latencyTestEnabled is false, testServer is '' when testServerEnabled is false, and ip_address is '0.0.0.0' with hostname '' when userInfoEnabled is false. Those empty strings are why the page normalizes before forwarding.
These describe the normalized payloads the WebView forwards, which is what SpeedTestScreen.tsx consumes:
interface SpeedTestResult {
download: number;
upload: number;
latency: number;
jitter: number;
testServer: string;
ip_address: string;
hostname: string;
}
interface SpeedTestProgress {
type: 'download' | 'upload' | 'latency';
currentSpeed: number; // Mbps, 0 on latency events
percentDone: number;
pass: number; // 0 during the latency phase
}
interface SpeedTestError {
code: number;
message: string;
}
1001 Invalid Account1002 Domain Mismatch2001 Test Error2002 Invalid server response2003 Request timeout2004 Test timeout2005 Speed test could not start (status N)2006 Speed test engine did not load1002 almost always means the page is not being served from your registered domain. 2006, and 2005 with status 0, mean the request never left the device, usually an ad blocker, a content blocker or a frame-src Content Security Policy that does not allow https://speedof.me. A 2005 carrying any other status is a real HTTP response: the request reached the network and came back refused.
Customize in speedtest.html:
SomApi.config.sustainTime = 6; // 1-8 seconds
SomApi.config.testServerEnabled = true;
SomApi.config.userInfoEnabled = true;
SomApi.config.latencyTestEnabled = true;
SomApi.config.uploadTestEnabled = true;
SomApi.config.progress.enabled = true;
SomApi.config.progress.verbose = true; // required for progress.currentSpeed
npx expo install react-native-webview
javaScriptEnabled={true}window.ReactNativeWebView exists in HTML