Skip to content

feat: add handleDeeplink function - #2

Open
sakkshm26 wants to merge 4 commits into
mainfrom
feat/handle-deeplink
Open

feat: add handleDeeplink function#2
sakkshm26 wants to merge 4 commits into
mainfrom
feat/handle-deeplink

Conversation

@sakkshm26

@sakkshm26 sakkshm26 commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added deeplink handling to capture re‑engagement attribution across platforms.
  • Chores

    • Bumped package version to 1.2.0.
    • Upgraded native Linkrunner SDKs for iOS and Android.
  • Behavior

    • Web platform returns a “not available” response and logs a platform-not-available warning for deeplink handling.
  • Documentation

    • Added CHANGELOG entry for version 1.2.0.

@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@sakkshm26 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 25 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 25 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e5cc16b-7730-4307-a4f5-d72d33bea039

📥 Commits

Reviewing files that changed from the base of the PR and between 91994a8 and 9e55713.

📒 Files selected for processing (3)
  • android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt
  • ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift
  • src/index.ts
📝 Walkthrough

Walkthrough

Adds a cross‑platform handleDeeplink() API for re‑engagement attribution, implements platform handlers (Android, iOS), adds a web stub, bumps package version to 1.2.0, and updates native SDK dependency constraints for iOS (CocoaPods/SwiftPM) and Android. (50 words)

Changes

Cohort / File(s) Summary
Version & Dependency Updates
CHANGELOG.md, package.json, CapacitorLinkrunner.podspec, Package.swift, android/build.gradle
Bumped package to 1.2.0. Updated native SDK constraints: CocoaPods LinkrunnerKit ~> 3.10.0, SwiftPM linkrunner-ios from: "3.10.0", Android SDK io.linkrunner:android-sdk:3.8.0.
Android Native Implementation
android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt
Added @PluginMethod fun handleDeeplink(call: PluginCall) that validates input, returns early for blank URLs, launches a coroutine to call SDK handleDeeplink, resolves with structured JSObject on success, and rejects with code HANDLE_DEEPLINK_FAILED on SDK failure/exception.
iOS Native Implementation
ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift
Added @objc func handleDeeplink(_ call: CAPPluginCall) and registered it in pluginMethods; validates URL, requires iOS 15+, awaits linkrunner.handleDeeplink(url:) in an async Task and resolves/rejects with structured payload or error messages.
TypeScript Interface & Implementation
src/definitions.ts, src/index.ts, src/web.ts
Added HandleDeeplinkOptions, HandleDeeplinkData, HandleDeeplinkResult types and handleDeeplink() to plugin interface; implemented Linkrunner.handleDeeplink(deeplinkUrl) with validation, init check, native call and error propagation; added web stub returning is_linkrunner: false.

Sequence Diagram

sequenceDiagram
    participant App as App
    participant JS as Linkrunner (JS)
    participant Native as Platform Plugin (iOS/Android)
    participant SDK as Linkrunner SDK

    App->>JS: handleDeeplink(url)
    alt url null/blank
        JS-->>App: return early (void, logged)
    else token not initialized
        JS-->>App: throw initialization error
    else
        JS->>Native: handleDeeplink({ deeplinkUrl })
        Native->>SDK: handleDeeplink(url)
        alt SDK success (data)
            SDK-->>Native: success(data)
            Native-->>JS: resolve({ msg, status, data })
        else SDK success (no data) or failure
            SDK-->>Native: error/empty
            Native-->>JS: reject("HANDLE_DEEPLINK_FAILED", message)
        end
        JS-->>App: promise settled (resolve/reject)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~23 minutes

Poem

🐇 I found a link along the trail,
I tapped the SDK, left a little trail,
Android, iOS, web in tow,
I hop and track where users go,
Reunions made — a tiny rabbit's tale.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add handleDeeplink function' directly and clearly summarizes the main change—addition of a new handleDeeplink method across multiple platform implementations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/handle-deeplink

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift (1)

261-278: Consider using guard let for consistency with other methods.

The force unwrap pattern on line 265 works due to short-circuit evaluation, but it differs from the idiomatic guard let pattern used elsewhere in this file (e.g., init, capturePayment, trackEvent).

♻️ Suggested refactor using guard
 `@objc` func handleDeeplink(_ call: CAPPluginCall) {
-    let deeplinkUrl = call.getString("deeplinkUrl")
-    
-    // Handle empty URLs gracefully (matches SDK behavior)
-    if deeplinkUrl == nil || deeplinkUrl!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
+    // Handle empty URLs gracefully (matches SDK behavior)
+    guard let deeplinkUrl = call.getString("deeplinkUrl"),
+          !deeplinkUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
         call.resolve()
         return
     }
     
     Task {
         if `#available`(iOS 15.0, *) {
             await linkrunner.handleDeeplink(url: deeplinkUrl)
             call.resolve()
         } else {
             call.reject("iOS 15.0 or later is required")
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift` around lines 261 - 278,
The current handleDeeplink method uses a nil check plus a forced unwrap on
deeplinkUrl; replace that with an idiomatic guard let that extracts and trims
the deeplink string (e.g., guard let url =
call.getString("deeplinkUrl")?.trimmingCharacters(in: .whitespacesAndNewlines),
!url.isEmpty else { call.resolve(); return }), then use the non-optional url
inside the Task and pass it to linkrunner.handleDeeplink(url: url) to preserve
behavior and consistency with other methods like init, capturePayment, and
trackEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt`:
- Around line 480-487: The deeplink failure handling calls to call.reject
currently pass the code first and message second; update both occurrences (the
one using result.exceptionOrNull() and the catch (e: Exception) block) so
call.reject is invoked with the message first and the error code second (e.g.,
call.reject(exception?.message ?: "HandleDeeplink failed",
"HANDLE_DEEPLINK_FAILED") and call.reject(e.message ?: "HandleDeeplink failed",
"HANDLE_DEEPLINK_FAILED")). Keep the Log.e(TAG, "HandleDeeplink failed", ...)
lines unchanged.

In `@src/web.ts`:
- Around line 55-57: handleDeeplink currently logs the full options payload
(async handleDeeplink) which can leak tokens/identifiers; remove the raw
console.warn of options and replace it with a safe/log-safe behavior—either omit
logging entirely or log a generic message and/or a redacted version (e.g., only
log that a deeplink was received and any non-sensitive fields or masked query
params). Update the async handleDeeplink(options: HandleDeeplinkOptions)
implementation to avoid printing options directly and ensure any logged fields
are explicitly whitelisted or redacted.

---

Nitpick comments:
In `@ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift`:
- Around line 261-278: The current handleDeeplink method uses a nil check plus a
forced unwrap on deeplinkUrl; replace that with an idiomatic guard let that
extracts and trims the deeplink string (e.g., guard let url =
call.getString("deeplinkUrl")?.trimmingCharacters(in: .whitespacesAndNewlines),
!url.isEmpty else { call.resolve(); return }), then use the non-optional url
inside the Task and pass it to linkrunner.handleDeeplink(url: url) to preserve
behavior and consistency with other methods like init, capturePayment, and
trackEvent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b77b7e4f-cc86-4508-8784-3bde1d8e1c3e

📥 Commits

Reviewing files that changed from the base of the PR and between 7a0b581 and ad2dbf0.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • CapacitorLinkrunner.podspec
  • Package.swift
  • android/build.gradle
  • android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt
  • ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift
  • package.json
  • src/definitions.ts
  • src/index.ts
  • src/web.ts

Comment thread android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt
Comment thread src/web.ts Outdated
@sakkshm26
sakkshm26 requested a review from Shofiya2003 April 3, 2026 08:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift`:
- Around line 262-273: The empty-deeplink guard currently calls call.resolve()
with no payload which yields undefined to callers expecting a
HandleDeeplinkResult; update the guard around deeplinkUrl in
LinkrunnerPlugin.swift to resolve with a typed no-op result object (e.g.,
include the expected keys like "msg" and "status") instead of an empty resolve;
locate the check for deeplinkUrl and the call.resolve() call, and return a
structured HandleDeeplinkResult-compatible payload (for example a message
indicating "empty deeplink" and a neutral status) so callers always receive the
expected shape.

In `@src/index.ts`:
- Around line 423-440: The code trims deeplinkUrl only for validation but
forwards the original possibly whitespace-padded string to the native layer;
update handleDeeplink so you normalize the URL once (e.g., const
normalizedDeeplink = deeplinkUrl.trim()) and use that normalized value in both
the empty check and the payload sent to this.plugin.handleDeeplink({
deeplinkUrl: normalizedDeeplink }) to ensure consistent, whitespace-free URLs
are forwarded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5cc24b02-a7f5-45e8-9551-754534f743c6

📥 Commits

Reviewing files that changed from the base of the PR and between 301c4fe and 91994a8.

📒 Files selected for processing (5)
  • android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt
  • ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift
  • src/definitions.ts
  • src/index.ts
  • src/web.ts
✅ Files skipped from review due to trivial changes (1)
  • src/definitions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.kt

Comment on lines +262 to +273
let deeplinkUrl = call.getString("deeplinkUrl")

// Handle empty URLs gracefully (matches SDK behavior)
if deeplinkUrl == nil || deeplinkUrl!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
call.resolve()
return
}

Task {
if #available(iOS 15.0, *) {
let deeplinkData = await linkrunner.handleDeeplink(url: deeplinkUrl)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return a typed no-op result instead of empty resolve for blank deeplink URLs.

Line 266 resolves without payload, which can return undefined to callers that expect a HandleDeeplinkResult object (msg/status).

💡 Proposed fix
 `@objc` func handleDeeplink(_ call: CAPPluginCall) {
-    let deeplinkUrl = call.getString("deeplinkUrl")
-    
-    // Handle empty URLs gracefully (matches SDK behavior)
-    if deeplinkUrl == nil || deeplinkUrl!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
-        call.resolve()
-        return
-    }
+    guard let deeplinkUrl = call.getString("deeplinkUrl")?
+        .trimmingCharacters(in: .whitespacesAndNewlines),
+          !deeplinkUrl.isEmpty else {
+        call.resolve([
+            "msg": "Empty deeplinkUrl ignored",
+            "status": 200,
+            "data": [
+                "is_linkrunner": false,
+                "deeplink": ""
+            ]
+        ])
+        return
+    }
     
     Task {
         if `#available`(iOS 15.0, *) {
             let deeplinkData = await linkrunner.handleDeeplink(url: deeplinkUrl)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift` around lines 262 - 273,
The empty-deeplink guard currently calls call.resolve() with no payload which
yields undefined to callers expecting a HandleDeeplinkResult; update the guard
around deeplinkUrl in LinkrunnerPlugin.swift to resolve with a typed no-op
result object (e.g., include the expected keys like "msg" and "status") instead
of an empty resolve; locate the check for deeplinkUrl and the call.resolve()
call, and return a structured HandleDeeplinkResult-compatible payload (for
example a message indicating "empty deeplink" and a neutral status) so callers
always receive the expected shape.

Comment thread src/index.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant