Mobile Integration
Integrate edrone tracking into your native iOS and Android mobile applications using the REST API.
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
| Aspect | Web (JavaScript) | Mobile (REST API) |
|---|---|---|
| Method | _edrone object | HTTP POST request |
| User identification | Cookies (automatic) | Email or user_id (manual) |
| sender_type | client (automatic) | server (required) |
| Offline handling | Browser handles | You must implement |
| Threading | Main thread OK | Use background thread |
Since mobile apps don't use cookies, you must identify users by:
email- when user is logged in or provides emailuser_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
- Check App ID - Verify you're using the correct App ID
- Check sender_type - Must be
"server"for mobile - Check required fields - See Required Fields
- Check network - Ensure the device has internet connectivity
User not identified
- Always include
emailoruser_idwhen 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_idformat as your web integration - Ensure
product_idsmatch your product feed
Complete event reference
All events available for mobile:
| Event | Required fields | Use case |
|---|---|---|
product_view | product_ids, product_titles, product_images, product_urls, product_category_ids, product_category_names | User views product |
category_view | product_category_ids, product_category_names | User browses category |
add_to_cart | Same as product_view | User adds to cart |
order | email, first_name, order_id, country, city, currencies, payment values, all product fields | Purchase complete |
subscribe | email, subscriber_status, customer_tags | Newsletter signup |
See API Reference for complete field documentation.