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.