iOS, Android and React Native
There is no mobile SDK to install. An app records events by posting the same JSON the web tracker posts, to the same endpoint, with one extra field that says it is an app.
POST https://app.stepmetrics.co/v1/collect
content-type: application/json
{
"siteId": "YOUR_SITE_ID",
"name": "pageview",
"path": "/checkout",
"client": { "platform": "ios", "device": "mobile" },
"props": { "appVersion": "2.1.0" }
} Why client is not optional for an app
Send it. Without it your traffic is filed as bot traffic and disappears from every default view, and nothing will tell you that is happening.
The reason is that bot filtering reads the user agent, and that test assumes a browser wrote
it. An empty user agent is a bot, because a real browser always sends one. A user agent
containing axios/ is a bot, because on the web it is. Both of those describe
ordinary mobile HTTP stacks: URLSession, OkHttp and React Native's
fetch may send nothing at all, and an app built on axios announces itself as
axios. Declaring client tells the server to judge you as an app instead.
It is a declaration, not an exemption. If you also send automation: true you are
still recorded as automated, and the pace-based check that runs after the fact still applies.
The fields an app sends
| Field | Required | Notes |
|---|---|---|
siteId | yes | Same id as the web tag. One site can take web and app traffic together. |
path | yes |
The screen, as a stable path: /checkout, /settings/billing.
Use the route, never a display title, or the same screen splits across languages.
|
client.platform | yes, for apps | ios or android. In React Native this is
Platform.OS exactly. It is recorded as the OS, so app traffic segments
beside web traffic instead of forming a second vocabulary. A backend reporting on a
person's behalf sends server - see From a server.
|
client.device | no | mobile or tablet. Omit it and the device shows as Unknown.
Nothing is guessed on your behalf.
|
name | no | Defaults to pageview. Custom events work exactly as on the web. |
props | no | Where app version, build number and framework belong. There are no dedicated fields for them, because a field the server accepted and then dropped would be worse than no field. |
revenueCents, currency | no | Minor units, so in-app purchase totals stay integral. |
React Native
import { Platform } from 'react-native';
export function track(path, name = 'pageview', props) {
return fetch('https://app.stepmetrics.co/v1/collect', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
siteId: YOUR_SITE_ID,
name,
path,
client: { platform: Platform.OS, device: Platform.isPad ? 'tablet' : 'mobile' },
props,
}),
});
} Swift
func track(path: String, name: String = "pageview") {
var req = URLRequest(url: URL(string: "https://app.stepmetrics.co/v1/collect")!)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try? JSONSerialization.data(withJSONObject: [
"siteId": yourSiteId,
"name": name,
"path": path,
"client": ["platform": "ios",
"device": UIDevice.current.userInterfaceIdiom == .pad ? "tablet" : "mobile"],
])
URLSession.shared.dataTask(with: req).resume()
} Kotlin
fun track(path: String, name: String = "pageview") {
val body = JSONObject(mapOf(
"siteId" to yourSiteId,
"name" to name,
"path" to path,
"client" to JSONObject(mapOf("platform" to "android", "device" to "mobile")),
)).toString().toRequestBody("application/json".toMediaType())
client.newCall(Request.Builder().url("https://app.stepmetrics.co/v1/collect").post(body).build())
.enqueue(/* fire and forget */)
} What is different from the web
- No API key. The endpoint is write-only and the site id is public, exactly as in the browser. Never ship a read key in an app; anyone can extract it.
- Visitors are counted per IP and HTTP client per day. A phone moving from cellular to wifi counts as a new visitor, and several devices behind one NAT with an identical client can collapse into one. That follows from storing no durable per-person id, which is the same trade the web side makes.
- No automatic screen tracking. Call it from your navigation listener. The
web tracker hooks
history.pushState; an app has no equivalent to hook. - Browser shows as Unknown. An app is not a browser, and inventing a name for it would put a guess where a fact belongs.