A privacy-focused iOS analytics SDK for Swift 6.2+ and iOS 17+. Track user flows, interactions, and app performance while minimizing App Store privacy declarations.
✅ Privacy-First Design
- No personally identifiable information (PII) collection
- Uses Apple's privacy-preserving frameworks (MetricKit)
- Minimizes App Store privacy impact
- All data stored locally with optional backend sync
✅ Comprehensive Tracking
- User interactions and flows
- Screen views and navigation
- App lifecycle events
- Performance metrics (via MetricKit)
- Custom events
- Error tracking
✅ Modern Swift
- Swift 6.2 with strict concurrency
- Actor-based thread safety
- Async/await throughout
- Type-safe property values
✅ Flexible Storage
- In-memory or file system storage
- Automatic cleanup and limits
- Optional backend synchronization
Add XiAnalytics to your project in Xcode:
- File → Add Package Dependencies
- Enter the repository URL
- Select the version you want to use
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/alirezarzg/xiAnalytics.git", from: "1.0.0")
]In your AppDelegate or App struct:
import XiAnalytics
@main
struct MyApp: App {
init() {
// Configure with default settings
XiAnalytics.shared.configure(with: .default)
// Or customize the configuration
let config = AnalyticsConfiguration(
maxStoredEvents: 10_000,
autoTrackLifecycle: true,
collectPerformanceMetrics: true,
backendURL: URL(string: "https://api.example.com/analytics"),
globalProperties: [
"app_name": .string("MyApp"),
"environment": .string("production")
],
logLevel: .warning
)
XiAnalytics.shared.configure(with: config)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}import XiAnalytics
// Track a screen view
await XiAnalytics.shared.trackScreen("HomeScreen")
// Track a button tap
await XiAnalytics.shared.trackButtonTap("login_button")
// Track a custom interaction
await XiAnalytics.shared.trackInteraction(
action: "swipe",
target: "card_stack",
properties: [
"direction": .string("left"),
"card_index": .int(3)
]
)
// Track a custom event
await XiAnalytics.shared.track(
name: "purchase_completed",
category: .custom,
properties: [
"product_id": .string("premium_subscription"),
"price": .double(9.99),
"currency": .string("USD")
]
)
// Track performance
await XiAnalytics.shared.trackPerformance(
metric: "api_response_time",
value: 1.234,
properties: [
"endpoint": .string("/api/users")
]
)
// Track errors
await XiAnalytics.shared.trackError(
errorType: "network",
message: "Failed to load user data",
properties: [
"status_code": .int(500)
]
)Create a ViewModifier for automatic screen tracking:
import SwiftUI
import XiAnalytics
struct AnalyticsScreenModifier: ViewModifier {
let screenName: String
func body(content: Content) -> some View {
content
.task {
await XiAnalytics.shared.trackScreen(screenName)
}
}
}
extension View {
func trackScreen(_ name: String) -> some View {
modifier(AnalyticsScreenModifier(screenName: name))
}
}
// Usage
struct HomeView: View {
var body: some View {
VStack {
Text("Home")
}
.trackScreen("HomeScreen")
}
}import UIKit
import XiAnalytics
class HomeViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Task {
await XiAnalytics.shared.trackScreen("HomeScreen")
}
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
Task {
await XiAnalytics.shared.trackButtonTap("login_button")
}
}
}let config = AnalyticsConfiguration(
// Maximum events to store locally (default: 10,000)
maxStoredEvents: 10_000,
// Auto-track app lifecycle events (default: true)
autoTrackLifecycle: true,
// Collect MetricKit performance data (default: true)
collectPerformanceMetrics: true,
// Batch size for network sync (default: 100)
syncBatchSize: 100,
// Session timeout in seconds (default: 1800 = 30 minutes)
sessionTimeoutSeconds: 1800,
// Optional backend URL for syncing
backendURL: URL(string: "https://api.example.com/analytics"),
// Global properties attached to all events
globalProperties: [
"app_version": .string("1.0.0"),
"environment": .string("production")
],
// Storage type (default: .fileSystem)
storageType: .fileSystem, // or .memory
// Log level (default: .warning)
logLevel: .debug // .none, .error, .warning, .info, .debug
)// Start a new session manually
await XiAnalytics.shared.startNewSession()
// End the current session
await XiAnalytics.shared.endSession()// Sync a batch of events
try await XiAnalytics.shared.sync()
// Sync all stored events
try await XiAnalytics.shared.syncAll()
// Flush queued events to storage
await XiAnalytics.shared.flush()let stats = await XiAnalytics.shared.getStatistics()
print("Stored events: \(stats.storedEvents)")
print("Queued events: \(stats.queuedEvents)")
print("Total events: \(stats.totalEvents)")// Clear all stored analytics data
try await XiAnalytics.shared.clearAllData()XiAnalytics automatically collects performance metrics using Apple's MetricKit framework when collectPerformanceMetrics is enabled. This includes:
- CPU Usage: Cumulative CPU time
- Memory: Peak and average memory usage
- App Launch Time: Time to first draw
- Battery: Energy consumption metrics
- Scrolling Performance: Hitch time ratio
- Crashes: Crash diagnostics
- Hangs: App hang detection
- Disk Writes: Excessive disk write warnings
All MetricKit data is collected in a privacy-preserving way and automatically converted to analytics events.
❌ User identifiers (no user IDs, email, names) ❌ Device identifiers (no IDFA, device UUID) ❌ Location data ❌ Contact information ❌ User content ❌ Browsing history
✅ App version and build number ✅ iOS version ✅ Device type (iPhone, iPad - generic model) ✅ Locale/language ✅ Event timestamps ✅ Performance metrics (via MetricKit) ✅ Custom event properties you define
With default settings, you should be able to declare:
Data Not Collected:
- Contact Info
- User Content
- Browsing History
- Search History
- Identifiers
- Location
- Sensitive Info
Data Collected (Not Linked to User):
- Product Interaction (analytics events)
- Crash Data (from MetricKit)
- Performance Data (from MetricKit)
- Other Diagnostic Data
Note: Always review your specific implementation and consult Apple's privacy guidelines.
If you provide a backendURL, events are sent as JSON POST requests:
[
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "screen_view",
"category": "screen_view",
"timestamp": "2024-01-15T10:30:00Z",
"sessionId": "987fcdeb-51a2-43d7-8f9e-123456789abc",
"properties": {
"screen_name": "HomeScreen"
}
}
]Expected response: HTTP 200-299 status code.
// Good
await XiAnalytics.shared.track(name: "checkout_completed", category: .custom)
// Avoid
await XiAnalytics.shared.track(name: "event1", category: .custom)await XiAnalytics.shared.trackScreen(
"ProductDetail",
properties: [
"product_category": .string("electronics"),
"price_range": .string("high"),
"in_stock": .bool(true)
]
)// User registration flow
await XiAnalytics.shared.trackScreen("RegistrationStart")
await XiAnalytics.shared.trackInteraction(action: "tap", target: "email_field")
await XiAnalytics.shared.trackInteraction(action: "tap", target: "password_field")
await XiAnalytics.shared.trackInteraction(action: "tap", target: "submit_button")
await XiAnalytics.shared.track(name: "registration_completed", category: .custom)XiAnalytics automatically flushes on app backgrounding/termination if autoTrackLifecycle is enabled. For manual control:
await XiAnalytics.shared.flush()// In your app, sync daily or when appropriate
Task {
try? await XiAnalytics.shared.sync()
}- iOS 17.0+
- Swift 6.0+
- Xcode 16.0+
MIT License - See LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and feature requests, please use the GitHub issue tracker.