# Documentation (/docs) Privacy-first mobile analytics for modern applications. Track user behavior, sessions, and events without compromising user privacy. ## Get Started Choose your platform to begin integrating Phase Analytics: * [**Expo**](/docs/get-started/expo) - Complete setup guide for Expo applications * [**React Native**](/docs/get-started/react-native) - Integration guide for React Native apps * [**Swift**](/docs/get-started/swift) - iOS SDK for SwiftUI and UIKit applications ## Concepts Learn about Phase Analytics features and workflows: * [**Team & Billing**](/docs/concepts/team-billing) - Manage team members and subscriptions * [**Publishing**](/docs/concepts/publishing) - App store privacy requirements and disclosures ## Privacy & Legal Understand how Phase handles your data: * [**Stored Data**](/docs/privacy/stored-data) - Data storage, retention, and GDPR rights * [**Privacy Policy**](/docs/privacy/privacy-policy) - Our privacy commitments * [**Terms of Service**](/docs/privacy/terms-of-service) - Terms and conditions ## Need Help? Contact us at [support@phase.sh](mailto:support@phase.sh) for assistance. # Swift (/docs/get-started/swift) import { Step, Steps } from 'fumadocs-ui/components/steps'; import { TypeTable } from 'fumadocs-ui/components/type-table'; import { Tab, Tabs, TabsList, TabsTrigger, TabsContent } from 'fumadocs-ui/components/tabs'; import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock'; ## Installation ### Add Swift Package Add Phase Analytics to your project using Swift Package Manager: 1. In Xcode, go to **File → Add Package Dependencies** 2. Enter the repository URL:

[https://github.com/Phase-Analytics/Phase-Swift](https://github.com/Phase-Analytics/Phase-Swift)

3. Select the latest version 4. Add to your target **Or add to Package.swift:** }> ```swift dependencies: [ .package(url: "https://github.com/Phase-Analytics/Phase-Swift", from: "0.1.3") ] ``` **Requirements:** * iOS 15.0+ or macOS 12.0+ * Swift 6.0+
### Get Your API Key 1. Sign in to [Phase Dashboard](https://phase.sh/dashboard) 2. Create a new project or select an existing one 3. Open API Keys tab 4. Copy your API Key (starts with `phase_`)
## Setup SwiftUI UIKit ### SwiftUI Wrap your app with the `Phase` view to initialize the SDK: }> ```swift import SwiftUI import PhaseAnalytics @main struct MyApp: App { var body: some Scene { WindowGroup { Phase(apiKey: "phase_xxx") { ContentView() } } } } ``` ### UIKit Initialize the SDK in your AppDelegate: }> ```swift 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") } return true } } ``` The SDK initialization is asynchronous. You must call `identify()` before tracking events. ## Configuration The `Phase` view and `initialize()` method accept the following parameters: ## Usage ### Identify User **Required:** Call `identify()` before using any other methods. This registers the device and starts a session. }> ```swift 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() } } } } ``` **Privacy by default:** * No personal data is collected without explicit properties * Device ID is auto-generated and stored locally * Only technical metadata is collected (OS version, platform, locale) **Adding custom properties:** You can optionally attach user properties. **Important:** If you add PII (personally identifiable information), ensure you have proper user consent: }> ```swift // After user login and consent await PhaseSDK.shared.identify([ "user_id": "123", "plan": "premium", "beta_tester": true ]) // ⚠️ If adding PII, get consent first let hasConsent = await customGetUserConsent() if hasConsent { await PhaseSDK.shared.identify([ "email": "user@example.com", "name": "John Doe" ]) } ``` Properties must be primitives: `String`, `Int`, `Double`, `Bool`, or `nil`. ### Track Events Track custom events with optional parameters. **Note:** `identify()` must be called first. }> ```swift // Event without parameters track("app_opened") // Event with parameters track("purchase_completed", [ "amount": 99.99, "currency": "USD", "product_id": "premium_plan" ]) // Using instance method PhaseSDK.shared.track("button_clicked", params: ["button_id": "submit"]) ``` **Event naming rules:** * Alphanumeric characters, underscores (`_`), hyphens (`-`), periods (`.`), and forward slashes (`/`) * 1-256 characters * Examples: `purchase`, `user.signup`, `payment/success` **Event parameters:** * Must be primitives: `String`, `Int`, `Double`, `Bool`, or `nil` ### Screen Tracking SwiftUI Manual Use the `.phaseScreen()` modifier to automatically track screen views: }> ```swift import SwiftUI import PhaseAnalytics struct ProfileView: View { let userID: String var body: some View { VStack { Text("Profile") } .phaseScreen("ProfileView", params: ["user_id": userID]) } } ``` **How it works:** Tracks screen view when the view first appears. Screen names are normalized automatically (e.g., `"ProfileView"` → `"/profile-view"`) with CamelCase converted to kebab-case. Supports optional parameters. Manually track screens using `trackScreen()`: }> ```swift // Using global function trackScreen("/profile", ["user_id": "123"]) // Using instance method PhaseSDK.shared.trackScreen("/settings", params: nil) ``` ## Type Reference ### DeviceProperties Custom user/device attributes passed to `identify()`: ### EventParams Event parameters passed to `track()`: ## How It Works ### Offline Support Events are queued locally using `UserDefaults` when offline. The queue automatically syncs when connection is restored. ### Privacy * No personal data is collected by default * Device IDs are generated locally and stored persistently * Geolocation is resolved server-side from IP address (disable with properties) * All data collection is optional via configuration ### Performance * 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 # React Native (/docs/get-started/react-native) import { Step, Steps } from 'fumadocs-ui/components/steps'; import { TypeTable } from 'fumadocs-ui/components/type-table'; import { Tab, Tabs, TabsList, TabsTrigger, TabsContent } from 'fumadocs-ui/components/tabs'; import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock'; ## Installation ### Install the SDK npm npm bun bun yarn yarn pnpm pnpm ```bash npm install phase-analytics ``` ```bash bun add phase-analytics ``` ```bash yarn add phase-analytics ``` ```bash pnpm add phase-analytics ``` ### Install Required Peer Dependencies Phase Analytics requires the following React Native packages to function properly: npm npm bun bun yarn yarn pnpm pnpm ```bash npm install @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info react-native-localize @react-navigation/native ``` ```bash bun add @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info react-native-localize @react-navigation/native ``` ```bash yarn add @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info react-native-localize @react-navigation/native ``` ```bash pnpm add @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info react-native-localize @react-navigation/native ``` These dependencies are **required** for the SDK to work correctly. The SDK uses: * `@react-native-async-storage/async-storage` - Local storage for offline event queuing * `@react-native-community/netinfo` - Network state monitoring * `react-native-device-info` - Device information collection * `react-native-localize` - Locale and timezone detection * `@react-navigation/native` - Automatic screen tracking (only needed if using `trackNavigation`) ### Install iOS Dependencies For iOS, install CocoaPods dependencies: ```bash cd ios && pod install ``` ### Get Your API Key 1. Sign in to [Phase Dashboard](https://phase.sh/dashboard) 2. Create a new project or select an existing one 3. Open API Keys tab 4. Copy your API Key (starts with `phase_`) ## Setup Wrap your app with the `PhaseProvider` component to initialize the SDK: }> ```tsx import { PhaseProvider } from 'phase-analytics/react-native'; import { NavigationContainer } from '@react-navigation/native'; export default function App() { return ( ); } ``` The `PhaseProvider` only initializes the SDK. You must call `Phase.identify()` before tracking events. ## Configuration The `PhaseProvider` component accepts the following props: ## Usage ### Identify User **Required:** Call `Phase.identify()` before using any other methods. This registers the device and starts a session. }> ```tsx import { Phase } from 'phase-analytics/react-native'; import { useEffect } from 'react'; export default function App() { useEffect(() => { // Initialize analytics - no PII collected by default Phase.identify(); }, []); return ; } ``` **Privacy by default:** * No personal data is collected without explicit properties * Device ID is auto-generated and stored locally * Only technical metadata is collected (OS version, platform, locale) **Adding custom properties:** You can optionally attach user properties. **Important:** If you add PII (personally identifiable information), ensure you have proper user consent: }> ```tsx // After user login and consent await Phase.identify({ user_id: '123', plan: 'premium', beta_tester: true }); // ⚠️ If adding PII, get consent first const hasConsent = await customGetUserConsent(); if (hasConsent) { await Phase.identify({ email: 'user@example.com', name: 'John Doe' }); } ``` Properties must be primitives: `string`, `number`, `boolean`, or `null`. ### Track Events Track custom events with optional parameters. **Note:** `Phase.identify()` must be called first. }> ```tsx // Event without parameters Phase.track('app_opened'); // Event with parameters Phase.track('purchase_completed', { amount: 99.99, currency: 'USD', product_id: 'premium_plan' }); ``` **Event naming rules:** * Alphanumeric characters, underscores (`_`), hyphens (`-`), periods (`.`), and forward slashes (`/`) * 1-256 characters * Examples: `purchase`, `user.signup`, `payment/success` **Event parameters:** * Must be primitives: `string`, `number`, `boolean`, or `null` ### Automatic Screen Tracking Enable automatic screen tracking by setting `trackNavigation` to `true` and passing a `navigationRef`: }> ```tsx import { PhaseProvider } from 'phase-analytics/react-native'; import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); export default function App() { const navigationRef = useNavigationContainerRef(); return ( ); } ``` **How it works:** * Requires `navigationRef` from `useNavigationContainerRef()` hook * Pass the ref to both `NavigationContainer` and `PhaseProvider` * Automatically tracks screen views on navigation state changes * Screen names are converted from route names * Works with nested navigators and dynamic routes **Important:** The `navigationRef` must be passed to both `NavigationContainer` and `PhaseProvider` for automatic tracking to work. ## Type Reference ### DeviceProperties Custom user/device attributes passed to `Phase.identify()`: ### EventParams Event parameters passed to `Phase.track()`: ## How It Works ### Offline Support Events are queued locally using `@react-native-async-storage/async-storage` when offline. The queue automatically syncs when connection is restored. ### Privacy * No personal data is collected by default * Device IDs are generated locally and stored persistently * Geolocation is resolved server-side from IP address (disable with properties) * All data collection is optional via configuration ### Performance * Offline events are batched and sent asynchronously * Network state is monitored via `@react-native-community/netinfo` * Failed requests retry with exponential backoff * Maximum batch size: 1000 events # Expo (/docs/get-started/expo) import { Step, Steps } from 'fumadocs-ui/components/steps'; import { TypeTable } from 'fumadocs-ui/components/type-table'; import { Tab, Tabs, TabsList, TabsTrigger, TabsContent } from 'fumadocs-ui/components/tabs'; import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock'; ## Installation ### Install the SDK npm npm bun bun yarn yarn pnpm pnpm ```bash npm install phase-analytics ``` ```bash bun add phase-analytics ``` ```bash yarn add phase-analytics ``` ```bash pnpm add phase-analytics ``` ### Install Required Peer Dependencies Phase Analytics requires the following Expo packages to function properly: npm npm bun bun yarn yarn pnpm pnpm ```bash npx expo install @react-native-async-storage/async-storage @react-native-community/netinfo expo-device expo-localization expo-router ``` ```bash bunx expo install @react-native-async-storage/async-storage @react-native-community/netinfo expo-device expo-localization expo-router ``` ```bash yarn dlx expo install @react-native-async-storage/async-storage @react-native-community/netinfo expo-device expo-localization expo-router ``` ```bash pnpm dlx expo install @react-native-async-storage/async-storage @react-native-community/netinfo expo-device expo-localization expo-router ``` These dependencies are **required** for the SDK to work correctly. The SDK uses: * `@react-native-async-storage/async-storage` - Local storage for offline event queuing * `@react-native-community/netinfo` - Network state monitoring * `expo-device` - Device information collection * `expo-localization` - Locale and timezone detection * `expo-router` - Automatic screen tracking ### Get Your API Key 1. Sign in to [Phase Dashboard](https://phase.sh/dashboard) 2. Create a new project or select an existing one 3. Open API Keys tab 4. Copy your API Key (starts with `phase_`) ## Setup Wrap your app with the `PhaseProvider` component in your root layout to initialize the SDK: }> ```tsx import { PhaseProvider } from 'phase-analytics/expo'; export default function RootLayout() { return ( ); } ``` The `PhaseProvider` only initializes the SDK. You must call `Phase.identify()` before tracking events. ## Configuration The `PhaseProvider` component accepts the following props: ## Usage ### Identify User **Required:** Call `Phase.identify()` before using any other methods. This registers the device and starts a session. }> ```tsx import { Phase } from 'phase-analytics/expo'; import { useEffect } from 'react'; export default function App() { useEffect(() => { // Initialize analytics - no PII collected by default Phase.identify(); }, []); return ; } ``` **Privacy by default:** * No personal data is collected without explicit properties * Device ID is auto-generated and stored locally * Only technical metadata is collected (OS version, platform, locale) **Adding custom properties:** You can optionally attach user properties. **Important:** If you add PII (personally identifiable information), ensure you have proper user consent: }> ```tsx // After user login and consent await Phase.identify({ user_id: '123', plan: 'premium', beta_tester: true }); // ⚠️ If adding PII, get consent first const hasConsent = await customGetUserConsent(); if (hasConsent) { await Phase.identify({ email: 'user@example.com', name: 'John Doe' }); } ``` Properties must be primitives: `string`, `number`, `boolean`, or `null`. ### Track Events Track custom events with optional parameters. **Note:** `Phase.identify()` must be called first. }> ```tsx // Event without parameters Phase.track('app_opened'); // Event with parameters Phase.track('purchase_completed', { amount: 99.99, currency: 'USD', product_id: 'premium_plan' }); ``` **Event naming rules:** * Alphanumeric characters, underscores (`_`), hyphens (`-`), periods (`.`), and forward slashes (`/`) * 1-256 characters * Examples: `purchase`, `user.signup`, `payment/success` **Event parameters:** * Must be primitives: `string`, `number`, `boolean`, or `null` ### Automatic Screen Tracking Enable automatic screen tracking by setting `trackNavigation` to `true` in your root layout. The SDK uses Expo Router's navigation hooks to track screen changes automatically: }> ```tsx import { PhaseProvider } from 'phase-analytics/expo'; import { Stack } from 'expo-router'; export default function RootLayout() { return ( ); } ``` **How it works:** * Wraps any Expo Router component (`Stack`, `Tabs`, `Drawer`, etc.) * Automatically tracks screen views on route changes * Screen names are derived from pathname * Works with nested routes and dynamic segments No additional setup required, just wrap your navigation structure with `PhaseProvider`. ## Type Reference ### DeviceProperties Custom user/device attributes passed to `Phase.identify()`: ### EventParams Event parameters passed to `Phase.track()`: ## How It Works ### Offline Support Events are queued locally using `@react-native-async-storage/async-storage` when offline. The queue automatically syncs when connection is restored. ### Privacy * No personal data is collected by default * Device IDs are generated locally and stored persistently * Geolocation is resolved server-side from IP address (disable with properties) * All data collection is optional via configuration ### Performance * Offline events are batched and sent asynchronously * Network state is monitored via `@react-native-community/netinfo` * Failed requests retry with exponential backoff * Maximum batch size: 1000 events # Team & Billing (/docs/concepts/team-billing) import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; ## Team Management Collaborate with your team by inviting members to your Phase project. Team members can view analytics and reports while the project owner maintains full control. ### Roles & Permissions **Owner** * Full control over the project * Rotate API keys * Delete or rename the project * Add and remove team members * Manage all project settings * Only the owner is billed for the project **Team Member** (Read-Only) * View analytics and reports * Access all data insights * Cannot modify project settings * Cannot manage API keys or team members * Does not affect billing ### Adding Team Members 1. Navigate to **[Dashboard](https://phase.sh/dashboard) → Application → Team** 2. Click **Add Member** 3. Enter the email address of the team member 4. They will receive an invitation to join the project **No Limits:** You can add unlimited team members to your project at no additional cost. ### Removing Team Members 1. Navigate to **[Dashboard](https://phase.sh/dashboard) → Application → Team** 2. Find the team member you want to remove 3. Click **Remove** 4. Confirm the action The removed member will immediately lose access to the project. ### Transfer Ownership Currently, ownership transfer is not supported. If you need to transfer a project to another user, please contact support. ## Billing Billing is managed through [Polar](https://polar.sh) and is tied to the project owner. ### How Billing Works * **Only the project owner is billed** for the project * Team members do not affect billing costs * Adding team members is free (unlimited) * Billing is based on your project's usage and plan ### Managing Your Subscription **For Project Owners:** The project owner can manage their subscription from the profile sidebar menu by selecting **Billing**. **For Team Members:** Team members can also access the Billing section from their profile, but this shows their personal Phase account billing, not the project's billing. Project billing is only visible to and managed by the project owner. ### Billing FAQ Team members are free and unlimited. Only the project owner is billed. Team members can access their own personal Phase account billing, but cannot view or manage the project's billing. Only the project owner can manage project billing. Your project will be downgraded to the free tier with limited features. Your data remains accessible. Billing is tied to the project owner. To transfer billing, you would need to transfer project ownership (contact support). ## Best Practices * **Invite trusted team members:** Only invite people who need access to your analytics data * **Regular audits:** Periodically review your team members and remove those who no longer need access * **API key security:** Only the owner can rotate API keys - ensure your owner account is secure * **Billing alerts:** Set up payment method backups to avoid service interruptions # Publishing (/docs/concepts/publishing) import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; import { Callout } from 'fumadocs-ui/components/callout'; When submitting your app to the App Store or Google Play, you'll need to disclose how Phase Analytics collects and uses data. This guide helps you accurately complete the required privacy forms. Phase is privacy-first and collects no personally identifiable information (PII) by default. All data collection is anonymous unless you explicitly identify users. ## iOS (Apple App Store) When submitting your app to the App Store, you'll need to complete Apple's App Privacy questionnaire. **Does your app collect data?** Select **Yes** - Phase collects anonymous analytics data. **Do you or your third-party partners collect data from this app?** Select **Yes, we collect data from this app** - Phase SDK collects analytics data. Select the following data types: **Product Interaction** * How users interact with your app (events, screen views, session duration) **Device ID** * Anonymous device identifiers for analytics purposes For each data type, select: * **Analytics** - To understand app usage and improve performance **Is this data linked to the user's identity?** Select **No** - Phase uses anonymous device IDs by default. Only select "Yes" if you explicitly call `Phase.identify()` with user-specific data like email or user ID. If you call `Phase.identify()` with personally identifiable information (email, name, user ID), you must select "Yes" for linking data to user identity and disclose which data types you're collecting. **Do you or your third-party partners use data from this app for tracking purposes?** Select **No** - Phase does not track users across apps and websites owned by other companies for targeted advertising or ad measurement purposes. ## Android (Google Play Store) When submitting your app to Google Play, you'll need to complete the Data Safety form. **Does your app collect or share any of the required user data types?** Select **Yes** - Phase collects device and app interaction data. **Is all of the user data collected by your app encrypted in transit?** Select **Yes** - All data sent to Phase is encrypted using HTTPS. Select the following categories: **Device or other IDs** * Anonymous device identifiers for analytics **App interactions** * How users interact with your app (events, screen views, session duration) For each data type, specify: * **Analytics** - To measure app usage and performance * **Data is collected but not shared** with third parties **Can users request that data be deleted?** Select **Yes** - Users can contact support to request data deletion. Phase automatically anonymizes data and does not collect PII by default. If you implement custom user identification with PII, update your disclosures accordingly. ## Best Practices * **Review before each release** - Privacy requirements may change with app store policy updates * **Match your implementation** - Only disclose data types you actually collect through Phase * **Document custom properties** - If you track custom user properties with PII, update your privacy policy * **Test before submission** - Review your app's data collection behavior in production builds * **Keep privacy policy updated** - Ensure your app's privacy policy accurately reflects Phase usage