feat: add handleDeeplink function - #2
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~23 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
ios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swift (1)
261-278: Consider usingguard letfor consistency with other methods.The force unwrap pattern on line 265 works due to short-circuit evaluation, but it differs from the idiomatic
guard letpattern 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
📒 Files selected for processing (10)
CHANGELOG.mdCapacitorLinkrunner.podspecPackage.swiftandroid/build.gradleandroid/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.ktios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swiftpackage.jsonsrc/definitions.tssrc/index.tssrc/web.ts
There was a problem hiding this comment.
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
📒 Files selected for processing (5)
android/src/main/java/io/linkrunner/capacitor/LinkrunnerPlugin.ktios/Sources/LinkrunnerPlugin/LinkrunnerPlugin.swiftsrc/definitions.tssrc/index.tssrc/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
| 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) | ||
|
|
There was a problem hiding this comment.
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.
Summary by CodeRabbit
New Features
Chores
Behavior
Documentation