Deep linking, attribution, and smart links for iOS.
Part of the Grovs open-source mobile linking platform.
Quick Start · API Reference · Full Docs
The Grovs iOS SDK provides deep linking, universal linking, link generation, in-app messaging, revenue tracking, and attribution for your iOS apps. It supports both Swift and Objective-C.
- Deep linking & universal links — route users to the right in-app screen, even after install
- Smart link generation — create trackable links with metadata, custom redirects, and UTM parameters
- In-app messaging — display messages and announcements from the Grovs dashboard
- Push notifications — receive push notifications for dashboard-sent messages
- Revenue tracking — log StoreKit 2 and custom purchases with automatic attribution
- User identity — attach user IDs and attributes for analytics and segmentation
- Self-hosting support — point the SDK at your own backend
- iOS 13.0+
- Swift 5.0+
- Xcode 14+
- In Xcode, go to File → Swift Packages → Add Package Dependency
- Enter the repository URL:
https://github.com/grovs-io/grovs-iOS.git - Select the version range that fits your project
- Click Next, then Finish
Add the pod to your Podfile:
pod 'Grovs'Then run:
pod installImport the module and configure the SDK in your AppDelegate:
import Grovs
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Grovs.configure(APIKey: "your-api-key", useTestEnvironment: false, delegate: self) { success in
if success {
print("Grovs SDK is ready")
}
}
// Optional: enable debug logging
Grovs.setDebug(level: .info)
// Optional: set user identity for analytics
Grovs.userIdentifier = "user_id_from_your_app"
Grovs.userAttributes = ["name": "John Doe", "plan": "premium"]
return true
}For self-hosted backends, pass the baseURL parameter (domain only — the SDK appends the API path):
Grovs.configure(APIKey: "your-api-key", useTestEnvironment: false, baseURL: "https://your-domain.com", delegate: self)func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
Grovs.handleSceneDelegate(openURLContexts: URLContexts)
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
Grovs.handleSceneDelegate(continue: userActivity)
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Grovs.handleSceneDelegate(options: connectionOptions)
}func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
return Grovs.handleAppDelegate(continue: userActivity, restorationHandler: restorationHandler)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return Grovs.handleAppDelegate(open: url, options: options)
}Conform to the GrovsDelegate protocol to receive deep link callbacks:
class YourViewController: UIViewController, GrovsDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Grovs.delegate = self
}
func grovsReceivedPayloadFromDeeplink(link: String?, payload: [String: Any]?, tracking: [String: Any]?) {
// Route the user based on payload data
if let screen = payload?["screen"] as? String {
navigateTo(screen)
}
}
}You can also retrieve past payloads:
// Get the most recent payload
Grovs.lastReceivedPayload { payload in
print("Last payload: \(payload)")
}
// Get all payloads received since app launch
Grovs.allReceivedPayloadsSinceStartup { payloads in
guard let payloads = payloads else { return }
for payload in payloads {
print("Payload: \(payload)")
}
}Create smart links with metadata, payload data, and tracking parameters:
Grovs.generateLink(
title: "Check out this product",
subtitle: "Limited time offer",
imageURL: "https://example.com/image.jpg",
data: ["productId": "12345", "screen": "product_detail"],
tags: ["promotion", "share"],
trackingCampaign: "spring_sale",
trackingSource: "in_app",
trackingMedium: "share_button"
) { url in
guard let url = url else { return }
print("Generated link: \(url)")
}Override where a link sends users on each platform:
let redirects = CustomRedirects(
ios: CustomLinkRedirect(link: "https://example.com/ios-promo"),
android: CustomLinkRedirect(link: "https://example.com/android-promo"),
desktop: CustomLinkRedirect(link: "https://example.com/desktop-promo", openAppIfInstalled: false)
)
Grovs.generateLink(title: "Special offer", data: ["promoId": "summer25"], customRedirects: redirects) { url in
guard let url = url else { return }
print("Generated link: \(url)")
}Present a share sheet after generating a link:
Grovs.generateLink(title: "Share this", data: ["itemId": "abc"]) { url in
guard let url = url else { return }
let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
self.present(activityVC, animated: true)
}If console messages have automatic display enabled in your dashboard, they will appear in your app without any additional integration.
To receive push notifications for messages sent from the Grovs dashboard:
1. Add capabilities — In Xcode, add the Push Notifications capability and enable Remote notifications under Background Modes.
2. Upload your APNs key — In Apple Developer → Keys, create a key with APNs enabled. Upload the .p8 file, Key ID, and Team ID in your Grovs dashboard under Settings → Push Notifications.
3. Request permission and register:
import UserNotifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}4. Pass the device token to Grovs:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
Grovs.pushToken = token
}Push notifications do not work in the iOS Simulator. Test on a physical device.
// Show the messages list as a modal
Grovs.displayMessagesViewController {
// Modal was dismissed
}
// Get unread count for badges
Grovs.numberOfUnreadMessages { count in
print("Unread: \(count)")
}Revenue tracking is currently in beta.
- Enable revenue tracking in the Grovs dashboard under Settings → Revenue Tracking
- Configure App Store Server Notifications in App Store Connect — set the production and sandbox URLs shown in the Grovs dashboard under Developers → iOS Setup → Revenue
import StoreKit
let result = try await Product.purchase(...)
if case .success(let verification) = result,
case .verified(let transaction) = verification {
Grovs.logInAppPurchase(transactionID: transaction.id) { success in
if success {
Task { await transaction.finish() }
}
}
}Requires iOS 15+. The SDK automatically extracts price, currency, and product info. Duplicates are filtered.
Grovs.logCustomPurchase(
type: .buy,
priceInCents: 999, // $9.99
currency: "USD",
productID: "premium_monthly"
) { success in
// Revenue event recorded
}Use .cancel and .refund transaction types for cancellations and refunds. For App Store purchases, these are detected automatically via App Store Server Notifications.
| Property | Type | Description |
|---|---|---|
delegate |
GrovsDelegate? |
Receives deep link callbacks |
userIdentifier |
String? |
User ID shown in dashboard and reports |
userAttributes |
[String: Any]? |
User attributes for analytics |
pushToken |
String? |
APNs device token for push notifications |
| Method | Description |
|---|---|
configure(APIKey:useTestEnvironment:baseURL:delegate:completion:) |
Initialize the SDK |
setSDK(enabled:) |
Enable or disable the SDK |
setDebug(level:) |
Set logging level (.info, .warn, .error) |
generateLink(...) |
Generate a smart link |
lastReceivedPayload(completion:) |
Get the last deep link payload |
allReceivedPayloadsSinceStartup(completion:) |
Get all payloads since launch |
linkDetails(path:completion:) |
Get details for a link path |
displayMessagesViewController(completion:) |
Show messages modal |
numberOfUnreadMessages(completion:) |
Get unread message count |
logInAppPurchase(transactionID:completion:) |
Log a StoreKit 2 purchase |
logCustomPurchase(type:priceInCents:currency:productID:startDate:completion:) |
Log a custom purchase |
Full API reference: docs.grovs.io/docs/sdk/ios/api-reference
A demo project is available at grovs-io/grovs-ios-example-app.
- Custom URL Scheme — configure deep link URL schemes
- Associated Domains — set up universal links
- Apple App Prefix — find your Team ID
- Bundle Identifier — find your bundle ID
Full documentation at docs.grovs.io.
For technical support and inquiries, contact support@grovs.io.
See LICENSE for details.