Privacy-first mobile analytics for iOS and macOS applications.
- Privacy by Default - No PII collected without explicit consent
- Offline Support - Events queued locally and synced when online
- SwiftUI Native - Built for modern Swift with async/await
- Type Safe - Full Swift 6 concurrency support
- Lightweight - Minimal impact on app size
- Self-Hostable - Run on your own infrastructure
- iOS 15.0+ or macOS 12.0+
- Swift 6.0+
- Xcode 15.0+
Add Phase Analytics to your project using Swift Package Manager:
- Go to File → Add Package Dependencies
- Enter the repository URL:
https://github.com/Phase-Analytics/Phase - Select the latest version
- Add to your target
dependencies: [
.package(url: "https://github.com/Phase-Analytics/Phase-Swift", from: "0.1.8")
]Then add it to your target dependencies:
.target(
name: "YourTarget",
dependencies: [
.product(name: "PhaseAnalytics", package: "Phase")
]
)Wrap your app with the Phase view to initialize the SDK:
import SwiftUI
import PhaseAnalytics
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
Phase(apiKey: "phase_xxx") {
ContentView()
}
}
}
}Then identify the user and track events:
import SwiftUI
import PhaseAnalytics
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.onAppear {
Task {
// Initialize analytics - no PII collected by default
await PhaseSDK.shared.identify()
// Track custom events
track("app_opened")
}
}
}
}Initialize the SDK in your AppDelegate:
import UIKit
import PhaseAnalytics
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Task {
try await PhaseSDK.shared.initialize(apiKey: "phase_xxx")
await PhaseSDK.shared.identify()
}
return true
}
}The Phase view and initialize() method accept the following parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey |
String |
Required | Your Phase API key (starts with phase_) |
baseURL |
String |
"https://api.phase.sh" |
Custom API endpoint for self-hosted deployments |
logLevel |
LogLevel |
.none |
Console logging level (.info, .warn, .error, .none) |
debugData |
Bool |
false |
Mark identify/events as debug data |
deviceInfo |
Bool |
true |
Collect device metadata (model, OS version, platform, app version as app_version) |
userLocale |
Bool |
true |
Collect user locale and timezone information |
Phase(
apiKey: "phase_xxx",
logLevel: .info,
debugData: false,
deviceInfo: true,
userLocale: true
) {
ContentView()
}Register the device and start a session. Must be called before tracking events.
// Basic usage - no PII collected
await PhaseSDK.shared.identify()
// With custom properties
await PhaseSDK.shared.identify([
"user_id": "123",
"plan": "premium",
"beta_tester": true
])Track custom events with optional parameters.
// Global function - event without parameters
track("app_opened")
// Global function - event with parameters
track("purchase_completed", [
"amount": 99.99,
"currency": "USD",
"product_id": "premium_plan"
])
// Instance method
PhaseSDK.shared.track("button_clicked", params: ["button_id": "submit"])Manually track screen views.
// Global function
trackScreen("/profile", ["user_id": "123"])
// Instance method
PhaseSDK.shared.trackScreen("/settings", params: nil)Automatically track screen views in SwiftUI:
struct ProfileView: View {
let userID: String
var body: some View {
VStack {
Text("Profile")
}
.phaseScreen("ProfileView", params: ["user_id": userID])
}
}Screen names are normalized automatically (e.g., "ProfileView" → "/profile-view").
For complete documentation, including:
- Advanced configuration
- Event tracking best practices
- Screen tracking strategies
- Type references
- Privacy guidelines
Visit our documentation:
Swift Guide - Complete setup guide for iOS and macOS
Phase Analytics is designed with privacy as a core principle:
- No personal data is collected by default
- Device IDs are generated locally and stored persistently using
UserDefaults - Only technical metadata is collected (OS version, platform, locale, app version)
- Geolocation is resolved server-side from IP address
- All data collection is optional via configuration
Important: If you collect PII (personally identifiable information), ensure you have proper user consent.
Events are queued locally using UserDefaults when offline. The queue automatically syncs when connection is restored.
- Offline events are batched and sent asynchronously
- Network state is monitored automatically
- Failed requests retry with exponential backoff
- Maximum batch size: 1000 events
- Thread-safe with Swift 6 concurrency
All parameters must be primitives: String, Int, Double, Bool, or nil.
- Alphanumeric characters, underscores (
_), hyphens (-), periods (.), forward slashes (/), and spaces - 1-256 characters
- Examples:
purchase,user.signup,payment/success,Button Clicked
Phase Analytics uses the following dependencies:
- ULID.swift - Universally Unique Lexicographically Sortable Identifiers
- GzipSwift - Data compression
This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.
- Homepage: phase.sh
- GitHub: Phase-Analytics/Phase
- Issues: Report a bug
Contributions are welcome! Please feel free to submit a Pull Request.