-
Notifications
You must be signed in to change notification settings - Fork 38
Adds UIKit / RN docs #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds UIKit / RN docs #235
Conversation
WalkthroughAdds React Native support to mobile deep-link and installation docs alongside iOS, introduces new RN installation/initialization snippets, expands iOS UIKit/SwiftUI examples, updates navigation (docs.json) with Beta tags and RN guide, and augments quickstarts with first-launch handling, deep-link processing, and trackOpen flows. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant App
participant SDK as Dub SDK
participant OS as OS/Linking
participant Dest as Destination
rect rgba(230,245,255,0.5)
Note over App,SDK: App start
User->>App: Launch app
App->>SDK: init(publishableKey, domain)
end
alt First launch with initial deep link
OS-->>App: getInitialURL()
App->>SDK: trackOpen(deepLink)
SDK-->>App: { destinationUrl? }
App->>Dest: Navigate if destinationUrl present
else Subsequent opens via URL event
OS-->>App: url event (deepLink)
App->>SDK: trackOpen(deepLink)
SDK-->>App: { destinationUrl? }
App->>Dest: Navigate if destinationUrl present
end
opt Errors
SDK-->>App: error
App->>App: Handle/log error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
5a9a068
to
20a4123
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (7)
snippets/steps/initialize-react-native-sdk.mdx (1)
28-28
: Add missing import for useEffect hook.The manual initialization example uses
useEffect
but doesn't import it from React.Apply this fix:
+import React, { useEffect } from "react"; import dub from "@dub/react-native";
snippets/dub-client-react-native-install.mdx (2)
125-138
: Lead tracking snippet uses await at top levelWrap in an async function to be copy‑pasteable.
Apply this diff:
-try { - await dub.trackLead({ - eventName: "User Sign Up", - customerExternalId: user.id, - customerName: user.name, - customerEmail: user.email, - }); -} catch (error) { - // Handle sale tracking error -} +async function trackLead(user: { id: string; name: string; email: string }) { + try { + await dub.trackLead({ + eventName: "User Sign Up", + customerExternalId: user.id, + customerName: user.name, + customerEmail: user.email, + }); + } catch (error) { + // Handle lead tracking error + } +}
150-163
: Sale tracking snippet uses await at top levelWrap in an async function for correctness.
Apply this diff:
-try { - await dub.trackSale({ - customerExternalId: user.id, - amount: product.price.amount, - currency: "usd", - eventName: "Purchase", - }); -} catch (error) { - // Handle sale tracking error -} +async function trackSale( + user: { id: string }, + product: { price: { amount: number } } +) { + try { + await dub.trackSale({ + customerExternalId: user.id, + amount: product.price.amount, + currency: "usd", + eventName: "Purchase", + }); + } catch (error) { + // Handle sale tracking error + } +}snippets/dub-client-ios-install.mdx (2)
15-17
: macOS version is incorrect“macOS 10.13 (Ventura)+” mixes version and codename. Ventura is macOS 13.
Apply this diff:
-- macOS 10.13 (Ventura)+ +- macOS 13 (Ventura)+
168-176
: Consider SceneDelegate URL handling for scene-based appsIf the app uses scenes (iOS 13+), implement scene(:openURLContexts:) as well; application(:open:options:) may not fire.
concepts/deep-links/attribution.mdx (2)
31-35
: Outdated availability note conflicts with RN examples belowUpdate to reflect RN support (Beta) shown in this page.
Apply this diff:
-<Note> - This feature is currently only available for iOS (Swift). React Native and - Android support are coming soon. If you'd like early access, please [contact - us](https://dub.co/contact/support). -</Note> +<Note> + Available for iOS (Swift) and React Native (Beta). Android support is coming soon. If you'd like early access, please [contact us](https://dub.co/contact/support). +</Note>
269-297
: RN conversion helpers should be asyncFunctions call await but aren’t declared async.
Apply this diff:
-function trackLead(user: User) { +async function trackLead(user: User) { @@ -} +} @@ -function trackSale(user: User, product: Product) { +async function trackSale(user: User, product: Product) { @@ -} +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
concepts/deep-links/attribution.mdx
(6 hunks)concepts/deep-links/quickstart.mdx
(2 hunks)docs.json
(1 hunks)sdks/client-side-mobile/installation-guides/react-native.mdx
(1 hunks)sdks/client-side-mobile/installation-guides/swift.mdx
(2 hunks)sdks/client-side-mobile/introduction.mdx
(2 hunks)snippets/dub-client-ios-install.mdx
(5 hunks)snippets/dub-client-mobile-installation-guides.mdx
(1 hunks)snippets/dub-client-react-native-install.mdx
(1 hunks)snippets/steps/initialize-ios-sdk.mdx
(1 hunks)snippets/steps/initialize-react-native-sdk.mdx
(1 hunks)snippets/steps/install-react-native-sdk.mdx
(1 hunks)
🔇 Additional comments (17)
sdks/client-side-mobile/installation-guides/swift.mdx (4)
4-4
: LGTM! Proper Beta tagging applied.The addition of the "Beta" tag aligns with the PR's objective to mark iOS SDK as beta status.
7-7
: LGTM! Updated import path for iOS-specific content.The change from
dub-client-mobile-install.mdx
todub-client-ios-install.mdx
correctly specializes the import for iOS-specific installation instructions.
15-17
: LGTM! Improved clarity in domain selection instructions.The updated wording provides clearer guidance by directing users to select their domain from the Custom Domains settings page.
40-47
: LGTM! Added UIKit example card.The addition of the Swift (UIKit) card provides developers with reference implementation for UIKit-based applications, complementing the existing SwiftUI example.
snippets/steps/install-react-native-sdk.mdx (1)
1-14
: LGTM! Well-structured installation step.The installation step provides clear, comprehensive instructions for installing the Dub React Native SDK using npm, yarn, and pnpm package managers.
docs.json (2)
179-179
: LGTM! Appropriate Beta tagging for mobile SDK group.The Beta tag correctly indicates the maturity level of the Client-side SDK (Mobile) functionality.
185-186
: LGTM! Added React Native installation guide to navigation.The addition of the React Native installation guide entry properly expands the mobile SDK documentation coverage.
snippets/steps/initialize-ios-sdk.mdx (1)
29-50
: LGTM! Added comprehensive UIKit initialization example.The UIKit initialization example provides a complete implementation showing how to initialize the Dub SDK in an AppDelegate-based application. The code follows iOS conventions with proper AppDelegate structure and initialization in
application:didFinishLaunchingWithOptions:
.snippets/steps/initialize-react-native-sdk.mdx (1)
1-39
: LGTM! Comprehensive React Native initialization options.The initialization step provides two clear options for developers:
- Provider-based approach using
DubProvider
(recommended for most use cases)- Manual initialization approach using
dub.init()
inuseEffect
Both examples are well-documented and follow React Native best practices.
snippets/dub-client-mobile-installation-guides.mdx (1)
9-15
: LGTM! Added React Native installation guide card.The React Native card follows the same structure as the existing Swift card and provides appropriate navigation to the React Native installation guide.
sdks/client-side-mobile/introduction.mdx (2)
13-14
: LGTM! Updated SDK listings with Beta indicators.The changes appropriately:
- Mark both iOS and React Native SDKs as beta
- Update the messaging to indicate Android is coming soon (instead of React Native)
40-55
: LGTM! Added comprehensive example cards.The new example cards provide developers with GitHub repositories for both UIKit and React Native implementations, expanding from the original SwiftUI-only example.
sdks/client-side-mobile/installation-guides/react-native.mdx (5)
1-5
: LGTM! Proper front matter with Beta tag.The front matter correctly establishes the React Native guide with appropriate title, description, and Beta tag.
7-9
: LGTM! Well-structured imports.The imports correctly reference the React Native-specific installation snippet and shared verification components.
15-17
: LGTM! Clear prerequisite instructions.The prerequisites section provides clear guidance for obtaining the publishable key and domain from the appropriate settings pages.
31-40
: LGTM! Appropriate example card with GitHub link.The example card provides developers with a reference React Native implementation hosted on GitHub.
25-25
: React Native installation snippet exists and is valid.
Found /snippets/dub-client-react-native-install.mdx; it contains install, init, and tracking (trackOpen/trackLead/trackSale) examples.
import GeneratePublishableKeyStep from "/snippets/steps/generate-publishable-key.mdx"; | ||
import AllowlistDomainsStep from "/snippets/steps/allowlist-domains.mdx"; | ||
import InstallationGuides from "/snippets/dub-client-mobile-installation-guides.mdx"; | ||
import InstallationGuides from "/snippets/dub-client-ios-installation-guides.mdx"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify snippet import path exists
Ensure /snippets/dub-client-ios-installation-guides.mdx is present (name suggests iOS‑only; you may have renamed it).
Run:
🏁 Script executed:
#!/bin/bash
fd -a 'dub-client-*-installation-guides.mdx' | sed -n '1,200p'
Length of output: 0
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== attribution.mdx (first 200 lines) ==="
if [ -f concepts/deep-links/attribution.mdx ]; then
sed -n '1,200p' concepts/deep-links/attribution.mdx
else
echo "MISSING: concepts/deep-links/attribution.mdx"
fi
echo
echo "=== snippets directory listing ==="
if [ -d snippets ]; then
ls -la snippets | sed -n '1,200p'
else
echo "MISSING: snippets/ directory"
fi
echo
echo "=== Searching repo for matching snippet filenames and references ==="
rg -n --hidden --no-ignore-vcs -S 'dub-client-.*installation-guides.*\.mdx' || true
rg -n --hidden --no-ignore-vcs -S 'installation-guides' || true
rg -n --hidden --no-ignore-vcs -S 'import InstallationGuides' || true
Length of output: 11766
Broken snippet import — update import path
concepts/deep-links/attribution.mdx imports "/snippets/dub-client-ios-installation-guides.mdx" (line 10) but that file does not exist; replace with "/snippets/dub-client-mobile-installation-guides.mdx" (present) or with "/snippets/dub-client-ios-install.mdx" if you intended an iOS‑only snippet.
Location: concepts/deep-links/attribution.mdx:10
🤖 Prompt for AI Agents
In concepts/deep-links/attribution.mdx around line 10, the import path
"/snippets/dub-client-ios-installation-guides.mdx" points to a non-existent
file; replace it with the correct snippet path. Update the import to either
"/snippets/dub-client-mobile-installation-guides.mdx" if you want the mobile
installation guides, or "/snippets/dub-client-ios-install.mdx" if you intended
an iOS-only snippet; make the change on line 10 so the file imports an existing
snippet.
```typescript React Native expandable | ||
import { useState, useEffect, useRef } from "react"; | ||
import { Linking } from "react-native"; | ||
import AsyncStorage from "@react-native-async-storage/async-storage"; | ||
import dub from "@dub/react-native"; | ||
|
||
export default function App() { | ||
useEffect(() => { | ||
dub.init({ | ||
publishableKey: "<DUB_PUBLISHABLE_KEY>", | ||
domain: "<DUB_DOMAIN>", | ||
}); | ||
|
||
// Check if this is first launch | ||
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | ||
|
||
if (isFirstLaunch === null) { | ||
await handleFirstLaunch(); | ||
await AsyncStorage.setItem("is_first_launch", "false"); | ||
} else { | ||
// Handle initial deep link url (Android only) | ||
const url = await Linking.getInitialURL(); | ||
|
||
if (url) { | ||
await handleDeepLink(url); | ||
} | ||
} | ||
|
||
const linkingListener = Linking.addEventListener("url", (event) => { | ||
handleDeepLink(event.url); | ||
}); | ||
|
||
return () => { | ||
linkingListener.remove(); | ||
}; | ||
}, []); | ||
|
||
const handleFirstLaunch = async ( | ||
deepLinkUrl?: string | null | undefined | ||
): Promise<void> => { | ||
try { | ||
const response = await dub.trackOpen(deepLinkUrl); | ||
|
||
const destinationURL = response.link?.url; | ||
// Navigate to the destination URL | ||
} catch (error) { | ||
// Handle error | ||
} | ||
}; | ||
|
||
// Return your app... | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RN example: async useEffect and undefined handleDeepLink
Same issues as other RN snippets: await in non‑async useEffect and missing handleDeepLink.
Apply this diff to the effect:
useEffect(() => {
- dub.init({
- publishableKey: "<DUB_PUBLISHABLE_KEY>",
- domain: "<DUB_DOMAIN>",
- });
- // Check if this is first launch
- const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
- if (isFirstLaunch === null) {
- await handleFirstLaunch();
- await AsyncStorage.setItem("is_first_launch", "false");
- } else {
- // Handle initial deep link url (Android only)
- const url = await Linking.getInitialURL();
- if (url) {
- await handleDeepLink(url);
- }
- }
- const linkingListener = Linking.addEventListener("url", (event) => {
- handleDeepLink(event.url);
- });
- return () => {
- linkingListener.remove();
- };
+ const init = async () => {
+ dub.init({
+ publishableKey: "<DUB_PUBLISHABLE_KEY>",
+ domain: "<DUB_DOMAIN>",
+ });
+ const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
+ if (isFirstLaunch === null) {
+ await handleFirstLaunch();
+ await AsyncStorage.setItem("is_first_launch", "false");
+ } else {
+ const url = await Linking.getInitialURL();
+ if (url) await handleDeepLink(url);
+ }
+ };
+ init();
+ const linkingListener = Linking.addEventListener("url", (event) => {
+ handleDeepLink(event.url);
+ });
+ return () => linkingListener.remove();
}, []);
And add handleDeepLink:
const handleFirstLaunch = async (
@@
};
+
+ const handleDeepLink = async (deepLinkUrl: string): Promise<void> => {
+ try {
+ const response = await dub.trackOpen(deepLinkUrl);
+ const destinationURL = response.link?.url;
+ // Navigate to the destination URL
+ } catch (error) {
+ // Handle error
+ }
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```typescript React Native expandable | |
import { useState, useEffect, useRef } from "react"; | |
import { Linking } from "react-native"; | |
import AsyncStorage from "@react-native-async-storage/async-storage"; | |
import dub from "@dub/react-native"; | |
export default function App() { | |
useEffect(() => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
// Check if this is first launch | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
// Handle initial deep link url (Android only) | |
const url = await Linking.getInitialURL(); | |
if (url) { | |
await handleDeepLink(url); | |
} | |
} | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => { | |
linkingListener.remove(); | |
}; | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} | |
``` | |
import { useState, useEffect, useRef } from "react"; | |
import { Linking } from "react-native"; | |
import AsyncStorage from "@react-native-async-storage/async-storage"; | |
import dub from "@dub/react-native"; | |
export default function App() { | |
useEffect(() => { | |
const init = async () => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
const url = await Linking.getInitialURL(); | |
if (url) await handleDeepLink(url); | |
} | |
}; | |
init(); | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => linkingListener.remove(); | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
const handleDeepLink = async (deepLinkUrl: string): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} |
🤖 Prompt for AI Agents
In concepts/deep-links/attribution.mdx around lines 91–143, the useEffect
currently uses top-level await and references a missing handleDeepLink; fix by
making the effect synchronous and declaring an inner async function (e.g., async
function init()) that calls dub.init, reads
AsyncStorage.getItem("is_first_launch"), conditionally calls handleFirstLaunch
or Linking.getInitialURL()/handleDeepLink, and sets AsyncStorage when
appropriate, then invoke init() from useEffect; also add a handleDeepLink async
function (similar to handleFirstLaunch) that accepts a url string | null |
undefined, calls dub.trackOpen(url), extracts response.link?.url for navigation,
and handles errors; ensure the Linking event subscription is created and cleaned
up correctly (store subscription and call .remove() in the cleanup) and
await/await usage is confined to the inner async function and helper functions.
```swift iOS (UIKit) expandable | ||
// ViewController.swift | ||
import UIKit | ||
import Dub | ||
|
||
class ViewController: UIViewController { | ||
// View controller lifecycle | ||
|
||
private func trackLead(customerExternalId: String, name: String, email: String) { | ||
Task { | ||
do { | ||
let response = try await dub.trackLead(customerExternalId: customerExternalId, name: name, email: email) | ||
} catch let error as DubError { | ||
print(error.localizedDescription) | ||
} | ||
} | ||
} | ||
|
||
private func trackSale(customerExternalId: String, amount: Int, currency: String = "usd", eventName: String? = "Purchase", customerName: String? = nil, customerEmail: String? = nil, customerAvatar: String? = nil) { | ||
Task { | ||
do { | ||
let response = try await dub.trackSale(customerExternalId: customerExternalId, amount: amount, currency: currency, eventName: eventName, customerName: customerName, customerEmail: customerEmail, customerAvatar: customerAvatar) | ||
} catch let error as DubError { | ||
print(error.localizedDescription) | ||
} | ||
} | ||
} | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UIKit conversion examples reference undefined dub instance
Use Dub.shared in UIKit instead of dub.
Apply this diff:
- let response = try await dub.trackLead(customerExternalId: customerExternalId, name: name, email: email)
+ let response = try await Dub.shared.trackLead(customerExternalId: customerExternalId, name: name, email: email)
@@
- let response = try await dub.trackSale(customerExternalId: customerExternalId, amount: amount, currency: currency, eventName: eventName, customerName: customerName, customerEmail: customerEmail, customerAvatar: customerAvatar)
+ let response = try await Dub.shared.trackSale(customerExternalId: customerExternalId, amount: amount, currency: currency, eventName: eventName, customerName: customerName, customerEmail: customerEmail, customerAvatar: customerAvatar)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```swift iOS (UIKit) expandable | |
// ViewController.swift | |
import UIKit | |
import Dub | |
class ViewController: UIViewController { | |
// View controller lifecycle | |
private func trackLead(customerExternalId: String, name: String, email: String) { | |
Task { | |
do { | |
let response = try await dub.trackLead(customerExternalId: customerExternalId, name: name, email: email) | |
} catch let error as DubError { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
private func trackSale(customerExternalId: String, amount: Int, currency: String = "usd", eventName: String? = "Purchase", customerName: String? = nil, customerEmail: String? = nil, customerAvatar: String? = nil) { | |
Task { | |
do { | |
let response = try await dub.trackSale(customerExternalId: customerExternalId, amount: amount, currency: currency, eventName: eventName, customerName: customerName, customerEmail: customerEmail, customerAvatar: customerAvatar) | |
} catch let error as DubError { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
} | |
``` | |
// ViewController.swift | |
import UIKit | |
import Dub | |
class ViewController: UIViewController { | |
// View controller lifecycle | |
private func trackLead(customerExternalId: String, name: String, email: String) { | |
Task { | |
do { | |
let response = try await Dub.shared.trackLead(customerExternalId: customerExternalId, name: name, email: email) | |
} catch let error as DubError { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
private func trackSale(customerExternalId: String, amount: Int, currency: String = "usd", eventName: String? = "Purchase", customerName: String? = nil, customerEmail: String? = nil, customerAvatar: String? = nil) { | |
Task { | |
do { | |
let response = try await Dub.shared.trackSale(customerExternalId: customerExternalId, amount: amount, currency: currency, eventName: eventName, customerName: customerName, customerEmail: customerEmail, customerAvatar: customerAvatar) | |
} catch let error as DubError { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
} |
🤖 Prompt for AI Agents
In concepts/deep-links/attribution.mdx around lines 359 to 387, the UIKit
examples call an undefined variable dub; update those calls to use the shared
singleton (Dub.shared) instead (e.g., Dub.shared.trackLead(...) and
Dub.shared.trackSale(...)) so the examples compile; keep imports as-is and only
replace occurrences of dub with Dub.shared inside the Task blocks.
```typescript React Native expandable | ||
import { useState, useEffect, useRef } from "react"; | ||
import { Linking } from "react-native"; | ||
import AsyncStorage from "@react-native-async-storage/async-storage"; | ||
import dub from "@dub/react-native"; | ||
|
||
export default function App() { | ||
useEffect(() => { | ||
dub.init({ | ||
publishableKey: "<DUB_PUBLISHABLE_KEY>", | ||
domain: "<DUB_DOMAIN>", | ||
}); | ||
|
||
// Check if this is first launch | ||
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | ||
|
||
if (isFirstLaunch === null) { | ||
await handleFirstLaunch(); | ||
await AsyncStorage.setItem("is_first_launch", "false"); | ||
} else { | ||
// Handle initial deep link url (Android only) | ||
const url = await Linking.getInitialURL(); | ||
|
||
if (url) { | ||
await handleDeepLink(url); | ||
} | ||
} | ||
|
||
const linkingListener = Linking.addEventListener("url", (event) => { | ||
handleDeepLink(event.url); | ||
}); | ||
|
||
return () => { | ||
linkingListener.remove(); | ||
}; | ||
}, []); | ||
|
||
const handleFirstLaunch = async ( | ||
deepLinkUrl?: string | null | undefined | ||
): Promise<void> => { | ||
try { | ||
const response = await dub.trackOpen(deepLinkUrl); | ||
|
||
const destinationURL = response.link?.url; | ||
// Navigate to the destination URL | ||
} catch (error) { | ||
// Handle error | ||
} | ||
}; | ||
|
||
// Return your app... | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RN example: async useEffect and undefined handleDeepLink
Same fixes as above: wrap awaits in async init and add handleDeepLink.
Apply this diff to the effect:
useEffect(() => {
- dub.init({
- publishableKey: "<DUB_PUBLISHABLE_KEY>",
- domain: "<DUB_DOMAIN>",
- });
- // Check if this is first launch
- const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
- if (isFirstLaunch === null) {
- await handleFirstLaunch();
- await AsyncStorage.setItem("is_first_launch", "false");
- } else {
- // Handle initial deep link url (Android only)
- const url = await Linking.getInitialURL();
- if (url) {
- await handleDeepLink(url);
- }
- }
- const linkingListener = Linking.addEventListener("url", (event) => {
- handleDeepLink(event.url);
- });
- return () => {
- linkingListener.remove();
- };
+ const init = async () => {
+ dub.init({
+ publishableKey: "<DUB_PUBLISHABLE_KEY>",
+ domain: "<DUB_DOMAIN>",
+ });
+ const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
+ if (isFirstLaunch === null) {
+ await handleFirstLaunch();
+ await AsyncStorage.setItem("is_first_launch", "false");
+ } else {
+ const url = await Linking.getInitialURL();
+ if (url) await handleDeepLink(url);
+ }
+ };
+ init();
+ const linkingListener = Linking.addEventListener("url", (event) => {
+ handleDeepLink(event.url);
+ });
+ return () => linkingListener.remove();
}, []);
And add handleDeepLink:
const handleFirstLaunch = async (
@@
};
+
+ const handleDeepLink = async (deepLinkUrl: string): Promise<void> => {
+ try {
+ const response = await dub.trackOpen(deepLinkUrl);
+ const destinationURL = response.link?.url;
+ // Navigate to the destination URL
+ } catch (error) {
+ // Handle error
+ }
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```typescript React Native expandable | |
import { useState, useEffect, useRef } from "react"; | |
import { Linking } from "react-native"; | |
import AsyncStorage from "@react-native-async-storage/async-storage"; | |
import dub from "@dub/react-native"; | |
export default function App() { | |
useEffect(() => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
// Check if this is first launch | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
// Handle initial deep link url (Android only) | |
const url = await Linking.getInitialURL(); | |
if (url) { | |
await handleDeepLink(url); | |
} | |
} | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => { | |
linkingListener.remove(); | |
}; | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} | |
``` | |
import { useState, useEffect, useRef } from "react"; | |
import { Linking } from "react-native"; | |
import AsyncStorage from "@react-native-async-storage/async-storage"; | |
import dub from "@dub/react-native"; | |
export default function App() { | |
useEffect(() => { | |
const init = async () => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
const url = await Linking.getInitialURL(); | |
if (url) await handleDeepLink(url); | |
} | |
}; | |
init(); | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => linkingListener.remove(); | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
const handleDeepLink = async (deepLinkUrl: string): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} |
**Option 2**: Manually initialize the Dub SDK | ||
|
||
```typescript | ||
import dub from "@dub/react-native"; | ||
|
||
export default function App() { | ||
useEffect(() => { | ||
dub.init({ | ||
publishableKey: "<DUB_PUBLISHABLE_KEY>", | ||
domain: "<DUB_DOMAIN>", | ||
}); | ||
}, []); | ||
|
||
// Return your app... | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing React import for useEffect in manual init snippet
useEffect is used but not imported; snippet won’t compile as-is.
Apply this diff:
-import dub from "@dub/react-native";
+import { useEffect } from "react";
+import dub from "@dub/react-native";
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
**Option 2**: Manually initialize the Dub SDK | |
```typescript | |
import dub from "@dub/react-native"; | |
export default function App() { | |
useEffect(() => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
}, []); | |
// Return your app... | |
} | |
``` | |
**Option 2**: Manually initialize the Dub SDK | |
🤖 Prompt for AI Agents
In snippets/dub-client-react-native-install.mdx around lines 41 to 56 the
example calls useEffect but does not import it, which prevents the snippet from
compiling; add a top-level import for React and useEffect (e.g., import React, {
useEffect } from "react") to the code example so the hook is available in the
snippet.
export default function App() { | ||
useEffect(() => { | ||
dub.init({ | ||
publishableKey: "<DUB_PUBLISHABLE_KEY>", | ||
domain: "<DUB_DOMAIN>", | ||
}); | ||
|
||
// Check if this is first launch | ||
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | ||
|
||
if (isFirstLaunch === null) { | ||
await handleFirstLaunch(); | ||
await AsyncStorage.setItem("is_first_launch", "false"); | ||
} else { | ||
// Handle initial deep link url (Android only) | ||
const url = await Linking.getInitialURL(); | ||
|
||
if (url) { | ||
await handleDeepLink(url); | ||
} | ||
} | ||
|
||
const linkingListener = Linking.addEventListener("url", (event) => { | ||
handleDeepLink(event.url); | ||
}); | ||
|
||
return () => { | ||
linkingListener.remove(); | ||
}; | ||
}, []); | ||
|
||
const handleFirstLaunch = async ( | ||
deepLinkUrl?: string | null | undefined | ||
): Promise<void> => { | ||
try { | ||
const response = await dub.trackOpen(deepLinkUrl); | ||
|
||
const destinationURL = response.link?.url; | ||
// Navigate to the destination URL | ||
} catch (error) { | ||
// Handle error | ||
} | ||
}; | ||
|
||
// Return your app... | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RN example: async useEffect and undefined handleDeepLink
- await used directly in useEffect callback (invalid).
- handleDeepLink is referenced but not defined.
Apply this diff to fix both:
useEffect(() => {
- dub.init({
- publishableKey: "<DUB_PUBLISHABLE_KEY>",
- domain: "<DUB_DOMAIN>",
- });
-
- // Check if this is first launch
- const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
-
- if (isFirstLaunch === null) {
- await handleFirstLaunch();
- await AsyncStorage.setItem("is_first_launch", "false");
- } else {
- // Handle initial deep link url (Android only)
- const url = await Linking.getInitialURL();
-
- if (url) {
- await handleDeepLink(url);
- }
- }
-
- const linkingListener = Linking.addEventListener("url", (event) => {
- handleDeepLink(event.url);
- });
-
- return () => {
- linkingListener.remove();
- };
+ const init = async () => {
+ dub.init({
+ publishableKey: "<DUB_PUBLISHABLE_KEY>",
+ domain: "<DUB_DOMAIN>",
+ });
+
+ const isFirstLaunch = await AsyncStorage.getItem("is_first_launch");
+ if (isFirstLaunch === null) {
+ await handleFirstLaunch();
+ await AsyncStorage.setItem("is_first_launch", "false");
+ } else {
+ const url = await Linking.getInitialURL();
+ if (url) {
+ await handleDeepLink(url);
+ }
+ }
+ };
+ init();
+
+ const linkingListener = Linking.addEventListener("url", (event) => {
+ handleDeepLink(event.url);
+ });
+ return () => linkingListener.remove();
}, []);
And add handleDeepLink:
const handleFirstLaunch = async (
deepLinkUrl?: string | null | undefined
): Promise<void> => {
@@
};
+
+ const handleDeepLink = async (deepLinkUrl: string): Promise<void> => {
+ try {
+ const response = await dub.trackOpen(deepLinkUrl);
+ const destinationURL = response.link?.url;
+ // Navigate to the destination URL
+ } catch (error) {
+ // Handle error
+ }
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export default function App() { | |
useEffect(() => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
// Check if this is first launch | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
// Handle initial deep link url (Android only) | |
const url = await Linking.getInitialURL(); | |
if (url) { | |
await handleDeepLink(url); | |
} | |
} | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => { | |
linkingListener.remove(); | |
}; | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} | |
``` | |
export default function App() { | |
useEffect(() => { | |
const init = async () => { | |
dub.init({ | |
publishableKey: "<DUB_PUBLISHABLE_KEY>", | |
domain: "<DUB_DOMAIN>", | |
}); | |
const isFirstLaunch = await AsyncStorage.getItem("is_first_launch"); | |
if (isFirstLaunch === null) { | |
await handleFirstLaunch(); | |
await AsyncStorage.setItem("is_first_launch", "false"); | |
} else { | |
const url = await Linking.getInitialURL(); | |
if (url) { | |
await handleDeepLink(url); | |
} | |
} | |
}; | |
init(); | |
const linkingListener = Linking.addEventListener("url", (event) => { | |
handleDeepLink(event.url); | |
}); | |
return () => linkingListener.remove(); | |
}, []); | |
const handleFirstLaunch = async ( | |
deepLinkUrl?: string | null | undefined | |
): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
const handleDeepLink = async (deepLinkUrl: string): Promise<void> => { | |
try { | |
const response = await dub.trackOpen(deepLinkUrl); | |
const destinationURL = response.link?.url; | |
// Navigate to the destination URL | |
} catch (error) { | |
// Handle error | |
} | |
}; | |
// Return your app... | |
} |
DUB_DOMAIN
descriptionSummary by CodeRabbit