Privacy & Consent
Gate the Grovs iOS SDK on user consent, and see what it collects
Gating collection on consent
The SDK collects nothing while it is disabled. To start a launch disabled, pass your stored consent to configure — calling setSDK(enabled: false) afterwards is too late, because configuring starts the first round of collection:
let hasConsent = UserDefaults.standard.bool(forKey: "analyticsConsent")
Grovs.configure(
APIKey: "your-api-key",
useTestEnvironment: false,
enabled: hasConsent,
delegate: self
)Then turn it on or off as consent changes:
func userDidChangeConsent(_ granted: Bool) {
UserDefaults.standard.set(granted, forKey: "analyticsConsent")
Grovs.setSDK(enabled: granted)
}The flag is not persisted by the SDK — pass your stored value to configure on every launch.
A disabled configure calls its completion with false without attempting authentication. Treat that as "not started", not as a failure.
While the SDK is disabled
- Nothing leaves the device: no authentication, device lookup, events, screen views, or purchases.
- Automatic screen tracking stops.
- Features that need the backend are unavailable: deep link resolution and link generation are refused, and the messages list can still be presented but its requests are refused, so it stays empty.
- Changes to
userIdentifier,userAttributes,pushToken, and screen aliases are held. - Events and purchases already queued stay on disk.
When you enable it again, the SDK authenticates and sends everything that was held or queued.
What the SDK collects
| Data | When | Why |
|---|---|---|
| App version and build, bundle ID, device model, user agent, screen size, time zone, preferred language | When the SDK authenticates and looks up the device | Matching installs and opens to link clicks; analytics |
| Identifier for vendor (IDFV) and a Grovs device identifier stored in the keychain | Same | Recognizing the device across launches and reinstalls |
| User identifier, user attributes, push token | Only if you set them | Showing users in the dashboard; delivering messages |
| Installs, opens, time spent, reactivations | Automatically | Attribution and analytics |
| Custom events and screen views | When you call track / trackScreenView, and through automatic screen tracking | Analytics |
| Purchases | When you call logInAppPurchase or logCustomPurchase | Revenue analytics |
| Clipboard contents | Read once, on the first launch with the SDK (or a later launch if that first check could not complete), when your project has recent copy-to-clipboard link clicks and the clipboard holds a web URL. Sent only if it is a link on a Grovs host or one of your clipboardDomains | Deterministic install attribution — see clipboard-assisted deferred deep linking |
The SDK does not read the advertising identifier (IDFA) and does not require App Tracking Transparency.
Privacy manifest
The SDK ships a PrivacyInfo.xcprivacy for both Swift Package Manager and CocoaPods. It declares:
- Collected data types: Device ID, User ID, Product Interaction, and Purchase History, for app functionality and analytics. None is used for tracking.
- Required-reason APIs:
UserDefaults(reasonCA92.1).
Xcode merges it into your app's privacy report. To see the combined report, archive your app and choose Generate Privacy Report in the Organizer.
App Store privacy details
Your app's privacy details in App Store Connect must cover what the SDK collects on your behalf. Based on the table above, that is typically:
- Identifiers → Device ID
- Identifiers → User ID, if you set
userIdentifier - Usage Data → Product Interaction
- Purchases → Purchase History, if you log purchases
Once you set userIdentifier, Apple treats this data as linked to the user's identity. Anything you put in userAttributes or event properties — an email address or name, for example — is data you collect too, and needs its own declaration.
Logs
SDK logs go through os_log. In release builds, message content is marked private and appears as <private> in Console.app. The default level is .error; see setDebug.