Skip to main content

Mobile Integration

Integrate edrone tracking into your native iOS and Android mobile applications using the REST API.

Key takeaway

Mobile integration works exactly like backend integration - send HTTP POST requests to the same Trace API endpoint. No SDK required.

Overview

If your e-commerce store has a mobile app, you can track user behavior just like on the web. The integration uses the same Trace API endpoint:

POST https://api.edrone.me/trace

Key differences from web integration

AspectWeb (JavaScript)Mobile (REST API)
Method_edrone objectHTTP POST request
User identificationCookies (automatic)Email or user_id (manual)
sender_typeclient (automatic)server (required)
Offline handlingBrowser handlesYou must implement
ThreadingMain thread OKUse background thread
Important: User identification

Since mobile apps don't use cookies, you must identify users by:

  • email - when user is logged in or provides email
  • user_id - your platform's user identifier

Without identification, edrone cannot track the user across sessions.


iOS Integration (Swift)

Setup

No SDK installation needed. Use native URLSession for HTTP requests.

Helper class

import Foundation

class EdroneTracker {
static let shared = EdroneTracker()

private let appId = "YOUR_APP_ID"
private let endpoint = URL(string: "https://api.edrone.me/trace")!

private init() {}

func track(event: [String: String], completion: ((Bool) -> Void)? = nil) {
var params = event
params["app_id"] = appId
params["sender_type"] = "server"

var request = URLRequest(url: endpoint)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

let body = params
.map { "\($0.key)=\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $0.value)" }
.joined(separator: "&")

request.httpBody = body.data(using: .utf8)

URLSession.shared.dataTask(with: request) { _, response, error in
let success = (response as? HTTPURLResponse)?.statusCode == 200
DispatchQueue.main.async {
completion?(success && error == nil)
}
}.resume()
}
}

Track product view

func trackProductView(product: Product, userEmail: String?) {
var event: [String: String] = [
"action_type": "product_view",
"product_ids": product.id,
"product_titles": product.title,
"product_images": product.imageURL,
"product_urls": product.url,
"product_category_ids": product.categoryIds.joined(separator: "~"),
"product_category_names": product.categoryNames.joined(separator: "~")
]

if let email = userEmail {
event["email"] = email
}

EdroneTracker.shared.track(event: event)
}

Track order

func trackOrder(order: Order, user: User) {
let event: [String: String] = [
"action_type": "order",
"email": user.email,
"first_name": user.firstName,
"last_name": user.lastName ?? "",
"user_id": user.id,
"order_id": order.id,
"country": order.shippingAddress.country,
"city": order.shippingAddress.city,
"base_currency": "USD",
"order_currency": order.currency,
"base_payment_value": String(order.total),
"order_payment_value": String(order.total),
"product_ids": order.items.map { $0.productId }.joined(separator: "|"),
"product_titles": order.items.map { $0.title }.joined(separator: "|"),
"product_images": order.items.map { $0.imageURL }.joined(separator: "|"),
"product_urls": order.items.map { $0.url }.joined(separator: "|"),
"product_counts": order.items.map { String($0.quantity) }.joined(separator: "|"),
"product_category_ids": order.items.map { $0.categoryIds.joined(separator: "~") }.joined(separator: "|"),
"product_category_names": order.items.map { $0.categoryNames.joined(separator: "~") }.joined(separator: "|")
]

EdroneTracker.shared.track(event: event)
}

Track subscription

func trackSubscription(email: String, firstName: String?, source: String) {
var event: [String: String] = [
"action_type": "subscribe",
"email": email,
"subscriber_status": "1",
"customer_tags": source
]

if let name = firstName {
event["first_name"] = name
}

EdroneTracker.shared.track(event: event)
}

Android Integration (Kotlin)

Setup

Add OkHttp dependency to build.gradle:

implementation 'com.squareup.okhttp3:okhttp:4.12.0'

Helper class

import okhttp3.*
import kotlinx.coroutines.*

object EdroneTracker {
private const val APP_ID = "YOUR_APP_ID"
private const val ENDPOINT = "https://api.edrone.me/trace"
private val client = OkHttpClient()

suspend fun track(event: Map<String, String>): Boolean = withContext(Dispatchers.IO) {
try {
val formBuilder = FormBody.Builder()
.add("app_id", APP_ID)
.add("sender_type", "server")

event.forEach { (key, value) ->
formBuilder.add(key, value)
}

val request = Request.Builder()
.url(ENDPOINT)
.post(formBuilder.build())
.build()

val response = client.newCall(request).execute()
response.isSuccessful
} catch (e: Exception) {
false
}
}
}

Track product view

suspend fun trackProductView(product: Product, userEmail: String?) {
val event = mutableMapOf(
"action_type" to "product_view",
"product_ids" to product.id,
"product_titles" to product.title,
"product_images" to product.imageUrl,
"product_urls" to product.url,
"product_category_ids" to product.categoryIds.joinToString("~"),
"product_category_names" to product.categoryNames.joinToString("~")
)

userEmail?.let { event["email"] = it }

EdroneTracker.track(event)
}

Track order

suspend fun trackOrder(order: Order, user: User) {
val event = mapOf(
"action_type" to "order",
"email" to user.email,
"first_name" to user.firstName,
"last_name" to (user.lastName ?: ""),
"user_id" to user.id,
"order_id" to order.id,
"country" to order.shippingAddress.country,
"city" to order.shippingAddress.city,
"base_currency" to "USD",
"order_currency" to order.currency,
"base_payment_value" to order.total.toString(),
"order_payment_value" to order.total.toString(),
"product_ids" to order.items.joinToString("|") { it.productId },
"product_titles" to order.items.joinToString("|") { it.title },
"product_images" to order.items.joinToString("|") { it.imageUrl },
"product_urls" to order.items.joinToString("|") { it.url },
"product_counts" to order.items.joinToString("|") { it.quantity.toString() },
"product_category_ids" to order.items.joinToString("|") { it.categoryIds.joinToString("~") },
"product_category_names" to order.items.joinToString("|") { it.categoryNames.joinToString("~") }
)

EdroneTracker.track(event)
}

Track subscription

suspend fun trackSubscription(email: String, firstName: String?, source: String) {
val event = mutableMapOf(
"action_type" to "subscribe",
"email" to email,
"subscriber_status" to "1",
"customer_tags" to source
)

firstName?.let { event["first_name"] = it }

EdroneTracker.track(event)
}

Best practices

1. Always use background threads

Never block the UI thread with network requests:

// iOS - URLSession is async by default
URLSession.shared.dataTask(with: request) { ... }.resume()
// Android - use coroutines
viewModelScope.launch {
EdroneTracker.track(event)
}

2. Handle offline scenarios

Queue events when offline and send when connection is restored:

class EventQueue {
private var pendingEvents: [[String: String]] = []

func enqueue(event: [String: String]) {
if isOnline() {
EdroneTracker.shared.track(event: event)
} else {
pendingEvents.append(event)
}
}

func flush() {
pendingEvents.forEach { EdroneTracker.shared.track(event: $0) }
pendingEvents.removeAll()
}
}

3. Implement retry logic

suspend fun trackWithRetry(event: Map<String, String>, maxRetries: Int = 3): Boolean {
repeat(maxRetries) { attempt ->
if (EdroneTracker.track(event)) return true
delay(1000L * (attempt + 1)) // Exponential backoff
}
return false
}

4. URL-encode special characters

Product titles and URLs may contain special characters:

let encodedTitle = product.title.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
val encodedTitle = URLEncoder.encode(product.title, "UTF-8")

Troubleshooting

Events not appearing in Mission Control

  1. Check App ID - Verify you're using the correct App ID
  2. Check sender_type - Must be "server" for mobile
  3. Check required fields - See Required Fields
  4. Check network - Ensure the device has internet connectivity

User not identified

  • Always include email or user_id when available
  • Without identification, events are tracked but cannot be attributed to a specific customer

Request fails silently

  • Implement proper error handling and logging
  • Check HTTP response status code (should be 200)
  • Verify all field values are properly URL-encoded

Orders not matching web orders

  • Use the same order_id format as your web integration
  • Ensure product_ids match your product feed

Complete event reference

All events available for mobile:

EventRequired fieldsUse case
product_viewproduct_ids, product_titles, product_images, product_urls, product_category_ids, product_category_namesUser views product
category_viewproduct_category_ids, product_category_namesUser browses category
add_to_cartSame as product_viewUser adds to cart
orderemail, first_name, order_id, country, city, currencies, payment values, all product fieldsPurchase complete
subscribeemail, subscriber_status, customer_tagsNewsletter signup

See API Reference for complete field documentation.