Docs

Deep Linking

Handle incoming deep links in your iOS app with the Grovs SDK

Conform to the GrovsDelegate protocol to receive deep link callbacks:

Swift
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)
        }
    }
}

Grovs.delegate is held weakly. Assign an object that lives as long as your app — your AppDelegate or a long-lived coordinator. If the delegate is deallocated (for example, a view controller that gets dismissed), payloads stop arriving until you set a new one.

Set the delegate as early as possible, ideally by passing it to configure. If a link opens the app before configure runs — common when the SDK is configured from a React Native or Flutter bridge — the SDK keeps the most recent link and handles it once configured. Payloads delivered while no delegate was set can still be read with lastReceivedPayload.

Handling SDK errors

GrovsDelegate has an optional grovsDidEncounterError callback. It has a default no-op implementation, so existing delegates keep compiling — implement it to surface failures to your own logging:

Swift
func grovsDidEncounterError(_ error: GrovsError, message: String) {
    Analytics.log("grovs_error", ["code": error.description, "message": message])
}
CaseMeaning
.authenticationFailedAuthentication with the backend failed — check the API key and bundle ID.
.networkRequestFailedA request failed after retries. The SDK backs off and retries later.
.eventDispatchFailedAn event batch failed to send. The events stay queued.
.linkGenerationFailedLink generation failed.

Errors are informational — the SDK recovers on its own. Both delegate methods are called on the main thread.

Clipboard-assisted deferred deep linking

When a link with copy-to-clipboard enabled is opened in a browser, the preview page copies it to the visitor's clipboard before sending them to the App Store. On the first launch after install, the SDK reads it and attributes the install deterministically. The deferred link arrives through the same grovsReceivedPayloadFromDeeplink callback as a fingerprint match.

This works with no integration. The check is armed on the first launch with the SDK — a fresh install, or the first launch of an app update that adds Grovs — and runs once. If it cannot complete on that launch, because the device is offline for example, it is retried on a later launch or foreground until it resolves, and never again after that. iOS may show its system paste confirmation when it runs. The check is skipped entirely when the backend reports no recent clicks on clipboard-enabled links for your project (currently a 48-hour window), and for devices whose clipboard holds no web URL; otherwise the prompt can appear even when the URL on the clipboard is not a Grovs link. Denying it simply leaves the install unattributed.

If your project serves links from a custom domain, list it so the SDK recognizes your links — content on any other host is never read or sent:

Swift
Grovs.configure(
    APIKey: "your-api-key",
    useTestEnvironment: false,
    clipboardDomains: ["links.yourdomain.com"],
    delegate: self
)

Enable copy-to-clipboard per link with copyToClipboardiOS, or set the default for the whole project in the dashboard. The end-to-end flow, settings, and limits are described in Copy to Clipboard.

Retrieving past payloads

Get the most recent payload:

Swift
Grovs.lastReceivedPayload { payload in
    print("Last payload: \(payload)")
}

Get all payloads received since app launch:

Swift
Grovs.allReceivedPayloadsSinceStartup { payloads in
    guard let payloads = payloads else { return }
    for payload in payloads {
        print("Payload: \(payload)")
    }
}

Retrieve the details for a specific link path:

Swift
Grovs.linkDetails(path: "/my-link-path") { details in
    print("Link details: \(details)")
}
Edit this page on GitHubLast updated 2026-10-07