Skip to main content

iOS SDK Implementation Examples

This page provides practical examples of how to implement the Zeotap iOS SDK in common app scenarios.

Complete App Integration Example

AppDelegate Setup

import UIKit
import ZeotapCollect

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

// Configure and initialize Zeotap Collect SDK
setupZeotapSDK()

return true
}

private func setupZeotapSDK() {
var collectOptions = CollectOption().writeKey(value: getWriteKeyForEnvironment())
.logging(value: isDebugMode())
.optout(value: false)
.build()
Collect.initialize(option: collectOptions)

// Set up initial user properties
setInitialUserProperties()
}

private func getWriteKeyForEnvironment() -> String {
#if DEBUG
return "zt_ios_dev_1234567890abcdef"
#else
return "zt_ios_prod_1234567890abcdef"
#endif
}

private func isDebugMode() -> Bool {
#if DEBUG
return true
#else
return false
#endif
}

private func setInitialUserProperties() {
Collect.getInstance()?.setUserProperties([
"appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
"deviceModel": UIDevice.current.model,
"osVersion": UIDevice.current.systemVersion,
"installDate": getInstallDate()
])
}

private func getInstallDate() -> String {
if let installDate = UserDefaults.standard.object(forKey: "app_install_date") as? Date {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: installDate)
} else {
let installDate = Date()
UserDefaults.standard.set(installDate, forKey: "app_install_date")
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: installDate)
}
}
}

E-commerce App Example

Product Catalog Integration

import UIKit
import ZeotapCollect

class ProductListViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!

var products: [Product] = []
var category: String = ""

override func viewDidLoad() {
super.viewDidLoad()

// Track screen view
Collect.getInstance()?.setEventProperties( "Product List Viewed", [
"category": category,
"productCount": products.count
])
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// Track time spent on category page
startTime = Date()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

if let startTime = startTime {
let timeSpent = Date().timeIntervalSince(startTime)
Collect.getInstance()?.setEventProperties( "Time Spent on Category", [
"category": category,
"timeSpent": timeSpent
])
}
}

private var startTime: Date?
}

class ProductDetailViewController: UIViewController {

var product: Product!

override func viewDidLoad() {
super.viewDidLoad()

// Track product view
Collect.getInstance()?.setEventProperties( "Product Viewed", [
"productId": product.id,
"productName": product.name,
"category": product.category,
"price": product.price,
"currency": "USD",
"brand": product.brand,
"inStock": product.inStock
])
}

@IBAction func addToCartTapped(_ sender: UIButton) {
// Add to cart logic
CartManager.shared.addProduct(product)

// Track add to cart event
Collect.getInstance()?.setEventProperties( "Product Added to Cart", [
"productId": product.id,
"productName": product.name,
"price": product.price,
"currency": "USD",
"cartTotal": CartManager.shared.total,
"cartItemCount": CartManager.shared.itemCount
])

// Show confirmation
showAddToCartConfirmation()
}

@IBAction func shareTapped(_ sender: UIButton) {
Collect.getInstance()?.setEventProperties( "Product Shared", [
"productId": product.id,
"shareMethod": "iOS Share Sheet"
])

// Show share sheet
showShareSheet()
}
}

Shopping Cart Integration

import ZeotapCollect

class CartManager {

static let shared = CartManager()
private var items: [CartItem] = []

func addProduct(_ product: Product, quantity: Int = 1) {
if let existingIndex = items.firstIndex(where: { $0.product.id == product.id }) {
items[existingIndex].quantity += quantity
} else {
items.append(CartItem(product: product, quantity: quantity))
}

// Track cart updated
Collect.getInstance()?.setEventProperties( "Cart Updated", [
"action": "add",
"productId": product.id,
"quantity": quantity,
"cartTotal": total,
"cartItemCount": itemCount
])
}

func removeProduct(_ product: Product) {
items.removeAll { $0.product.id == product.id }

Collect.getInstance()?.setEventProperties( "Cart Updated", [
"action": "remove",
"productId": product.id,
"cartTotal": total,
"cartItemCount": itemCount
])
}

var total: Double {
return items.reduce(0) { $0 + ($1.product.price * Double($1.quantity)) }
}

var itemCount: Int {
return items.reduce(0) { $0 + $1.quantity }
}
}

class CheckoutViewController: UIViewController {

@IBAction func completePurchase(_ sender: UIButton) {
let orderId = generateOrderId()
let items = CartManager.shared.items

// Process payment...
processPayment { [weak self] success in
if success {
self?.trackPurchaseCompleted(orderId: orderId, items: items)
self?.clearCart()
self?.showPurchaseConfirmation()
} else {
self?.trackPurchaseFailed(orderId: orderId, items: items)
self?.showPurchaseError()
}
}
}

private func trackPurchaseCompleted(orderId: String, items: [CartItem]) {
let itemsData = items.map { item in
return [
"productId": item.product.id,
"productName": item.product.name,
"quantity": item.quantity,
"price": item.product.price,
"category": item.product.category
]
}

Collect.getInstance()?.setEventProperties( "Purchase Completed", [
"orderId": orderId,
"totalAmount": CartManager.shared.total,
"currency": "USD",
"itemCount": CartManager.shared.itemCount,
"items": itemsData,
"paymentMethod": selectedPaymentMethod,
"shippingMethod": selectedShippingMethod
])
}

private func trackPurchaseFailed(orderId: String, items: [CartItem]) {
Collect.getInstance()?.setEventProperties( "Purchase Failed", [
"orderId": orderId,
"totalAmount": CartManager.shared.total,
"currency": "USD",
"itemCount": CartManager.shared.itemCount,
"failureReason": "Payment processing failed"
])
}
}

User Authentication Example

import ZeotapCollect

class AuthenticationManager {

static let shared = AuthenticationManager()

func loginUser(email: String, password: String, completion: @escaping (Bool, User?) -> Void) {
// Perform login API call
APIManager.shared.login(email: email, password: password) { [weak self] result in
switch result {
case .success(let user):
self?.handleSuccessfulLogin(user: user)
completion(true, user)

case .failure(let error):
self?.handleFailedLogin(email: email, error: error)
completion(false, nil)
}
}
}

private func handleSuccessfulLogin(user: User) {
// Set user identities
Collect.getInstance()?.setUserIdentities([
"email": user.email,
"userId": user.id,
"phoneNumber": user.phoneNumber ?? ""
])

// Set user properties
Collect.getInstance()?.setUserProperties([
"accountType": user.accountType,
"signupDate": user.signupDate,
"isVerified": user.isVerified,
"subscription": user.subscription,
"totalOrders": user.totalOrders,
"lastLoginDate": Date()
])

// Track login event
Collect.getInstance()?.setEventProperties( "User Logged In", [
"method": "email_password",
"isFirstTime": user.isFirstTimeLogin
])

// Store user session
UserDefaults.standard.set(user.id, forKey: "current_user_id")
}

private func handleFailedLogin(email: String, error: Error) {
Collect.getInstance()?.setEventProperties( "Login Failed", [
"method": "email_password",
"error": error.localizedDescription,
"email": email.isEmpty ? "empty" : "provided"
])
}

func logoutUser() {
// Clear user session
UserDefaults.standard.removeObject(forKey: "current_user_id")

// Track logout
Collect.getInstance()?.setEventProperties( "User Logged Out")

// Clear user identities (optional)
// Collect.getInstance().clearUserIdentities()
}

func registerUser(email: String, password: String, userData: [String: Any], completion: @escaping (Bool, User?) -> Void) {
APIManager.shared.register(email: email, password: password, userData: userData) { [weak self] result in
switch result {
case .success(let user):
self?.handleSuccessfulRegistration(user: user)
completion(true, user)

case .failure(let error):
self?.handleFailedRegistration(email: email, error: error)
completion(false, nil)
}
}
}

private func handleSuccessfulRegistration(user: User) {
// Set user identities
Collect.getInstance()?.setUserIdentities([
"email": user.email,
"userId": user.id
])

// Set initial user properties
Collect.getInstance()?.setUserProperties([
"signupDate": Date(),
"accountType": "free",
"isVerified": false,
"signupMethod": "email"
])

// Track registration
Collect.getInstance()?.setEventProperties( "User Registered", [
"method": "email_password",
"referralSource": getReferralSource()
])
}

private func handleFailedRegistration(email: String, error: Error) {
Collect.getInstance()?.setEventProperties( "Registration Failed", [
"method": "email_password",
"error": error.localizedDescription
])
}

private func getReferralSource() -> String {
return UserDefaults.standard.string(forKey: "referral_source") ?? "direct"
}
}

Content App Example

import ZeotapCollect

class ArticleViewController: UIViewController {

var article: Article!
private var readStartTime: Date?
private var scrollProgress: Double = 0

override func viewDidLoad() {
super.viewDidLoad()

// Track article view
Collect.getInstance()?.setEventProperties( "Article Viewed", [
"articleId": article.id,
"title": article.title,
"category": article.category,
"author": article.author,
"publishDate": article.publishDate,
"wordCount": article.wordCount
])

readStartTime = Date()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

trackReadingSession()
}

private func trackReadingSession() {
guard let startTime = readStartTime else { return }

let readTime = Date().timeIntervalSince(startTime)

Collect.getInstance()?.setEventProperties( "Article Reading Session", [
"articleId": article.id,
"readTime": readTime,
"scrollProgress": scrollProgress,
"completed": scrollProgress > 0.8
])
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
let progress = scrollView.contentOffset.y / (scrollView.contentSize.height - scrollView.frame.height)
scrollProgress = max(0, min(1, progress))

// Track reading milestones
if scrollProgress >= 0.25 && !milestones.contains("25%") {
milestones.insert("25%")
trackReadingMilestone(milestone: "25%")
} else if scrollProgress >= 0.50 && !milestones.contains("50%") {
milestones.insert("50%")
trackReadingMilestone(milestone: "50%")
} else if scrollProgress >= 0.75 && !milestones.contains("75%") {
milestones.insert("75%")
trackReadingMilestone(milestone: "75%")
} else if scrollProgress >= 0.90 && !milestones.contains("completed") {
milestones.insert("completed")
trackReadingMilestone(milestone: "completed")
}
}

private var milestones: Set<String> = []

private func trackReadingMilestone(milestone: String) {
Collect.getInstance()?.setEventProperties( "Reading Milestone", [
"articleId": article.id,
"milestone": milestone,
"timeToMilestone": Date().timeIntervalSince(readStartTime ?? Date())
])
}

@IBAction func shareArticle(_ sender: UIButton) {
Collect.getInstance()?.setEventProperties( "Article Shared", [
"articleId": article.id,
"shareMethod": "iOS Share Sheet",
"readProgress": scrollProgress
])

showShareSheet()
}

@IBAction func likeArticle(_ sender: UIButton) {
Collect.getInstance()?.setEventProperties( "Article Liked", [
"articleId": article.id,
"readProgress": scrollProgress
])
}
}

Gaming App Example

import ZeotapCollect

class GameManager {

static let shared = GameManager()
private var currentLevel: Int = 1
private var gameStartTime: Date?

func startGame(level: Int) {
currentLevel = level
gameStartTime = Date()

Collect.getInstance()?.setEventProperties( "Game Started", [
"level": level,
"gameMode": getCurrentGameMode(),
"playerLevel": getPlayerLevel()
])
}

func completeLevel(score: Int, stars: Int) {
guard let startTime = gameStartTime else { return }

let playTime = Date().timeIntervalSince(startTime)

Collect.getInstance()?.setEventProperties( "Level Completed", [
"level": currentLevel,
"score": score,
"stars": stars,
"playTime": playTime,
"attempts": getLevelAttempts(level: currentLevel)
])

// Update user properties
Collect.getInstance()?.setUserProperties([
"highestLevel": max(getPlayerLevel(), currentLevel),
"totalScore": getTotalScore() + score,
"totalPlayTime": getTotalPlayTime() + playTime
])
}

func failLevel(reason: String) {
guard let startTime = gameStartTime else { return }

let playTime = Date().timeIntervalSince(startTime)

Collect.getInstance()?.setEventProperties( "Level Failed", [
"level": currentLevel,
"reason": reason,
"playTime": playTime,
"attempts": getLevelAttempts(level: currentLevel) + 1
])
}

func purchaseItem(itemId: String, price: Double, currency: String) {
Collect.getInstance()?.setEventProperties( "In-App Purchase", [
"itemId": itemId,
"price": price,
"currency": currency,
"playerLevel": getPlayerLevel(),
"currentLevel": currentLevel
])
}

private func getCurrentGameMode() -> String {
// Implementation to get current game mode
return "classic"
}

private func getPlayerLevel() -> Int {
return UserDefaults.standard.integer(forKey: "player_level")
}

private func getLevelAttempts(level: Int) -> Int {
return UserDefaults.standard.integer(forKey: "level_\(level)_attempts")
}

private func getTotalScore() -> Int {
return UserDefaults.standard.integer(forKey: "total_score")
}

private func getTotalPlayTime() -> TimeInterval {
return UserDefaults.standard.double(forKey: "total_play_time")
}
}

Best Practices Summary

  1. Initialize Early: Set up the SDK in AppDelegate.application(_:didFinishLaunchingWithOptions:)

  2. Use Meaningful Event Names: Choose descriptive, consistent event names

  3. Include Relevant Properties: Add contextual information to events

  4. Set User Properties: Update user properties when they change

  5. Handle Different Environments: Use different configurations for development and production

  6. Track Key User Journey Points: Focus on important user actions and milestones

  7. Respect Privacy: Always ensure proper consent before tracking

  8. Test Thoroughly: Verify events are being sent correctly in development