Skip to content

feat: unresolved_configs in Release Config - #380

Open
yuvrajjsingh0 wants to merge 2 commits into
mainfrom
feat/send-extended-true-ios
Open

feat: unresolved_configs in Release Config#380
yuvrajjsingh0 wants to merge 2 commits into
mainfrom
feat/send-extended-true-ios

Conversation

@yuvrajjsingh0

@yuvrajjsingh0 yuvrajjsingh0 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Enhancements
    • Release configuration requests now always support extended=true, appending or updating it while preserving any existing query parameters.
    • Extended release-config responses can include opaque unresolved_properties, which are cached across launches and returned through the iOS API.
  • Documentation
    • Documented the optional unresolved_properties field and added an iOS-only lifecycle event for when it changes.
  • Security
    • Improved secure decoding to support archived payloads containing multiple permitted types.
  • Tests
    • Added extensive coverage for caching, serialization, secure coding, and extended=true URL construction.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The release config URL now uses URLComponents. The code adds extended=true when the parameter is absent and validates both the parsed components and reconstructed URL.

Changes

Release config URL handling

Layer / File(s) Summary
Query parameter injection
airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift
The code parses the release config URL, adds extended=true when absent, reconstructs the URL, and validates the result.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: balaganesh-juspay

Poem

A rabbit checks the URL with care,
Adds one query leaf to the air,
extended=true joins the trail,
Guards confirm the links prevail.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title identifies the Release Config change involving unresolved configuration data, but it does not mention the primary iOS extended=true request behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/send-extended-true-ios

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.

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift`:
- Around line 1059-1075: Remove the registered timeoutObserver before returning
from both URL-validation guard failures in the release-config download flow.
Ensure each failure path deregisters the observer before invoking
completionHandler, preventing the timeout callback from invoking
completionHandler again.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cb05cc4f-b06d-4a04-9ab5-0add20267bf3

📥 Commits

Reviewing files that changed from the base of the PR and between a706e7f and bc70969.

📒 Files selected for processing (1)
  • airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift

Comment on lines +1059 to +1075
guard let baseUrl = URL(string: self.releaseConfigURL),
var urlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: false) else {
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}


var queryItems = urlComponents.queryItems ?? []
if !queryItems.contains(where: { $0.name == "extended" }) {
queryItems.append(URLQueryItem(name: "extended", value: "true"))
}
urlComponents.queryItems = queryItems

guard let manifestUrl = urlComponents.url else {
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Remove timeoutObserver before returning on guard failure.

Both guards at lines 1059-1063 and 1071-1074 call completionHandler and return early on failure. Neither removes timeoutObserver, which was registered at lines 1034-1055 and remains active after this function returns. If RELEASE_CONFIG_TIMEOUT_NOTIFICATION fires later, the observer closure calls completionHandler a second time with a tempManifest result. startDownload (lines 851-886) treats each completionHandler invocation as authoritative and mutates releaseConfigDownloadStatus, posts notifications, and calls updateConfig/tryDownloadingUpdate. A second invocation can drive that state machine twice with different results.

This leak pattern already existed for the original single-guard case. The new second guard adds a further return path with the same gap.

🔧 Proposed fix to remove the observer on both guard-failure paths
         guard let baseUrl = URL(string: self.releaseConfigURL),
               var urlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: false) else {
+            if let observer = timeoutObserver {
+                NotificationCenter.default.removeObserver(observer)
+            }
             completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
             return
         }

         var queryItems = urlComponents.queryItems ?? []
         if !queryItems.contains(where: { $0.name == "extended" }) {
             queryItems.append(URLQueryItem(name: "extended", value: "true"))
         }
         urlComponents.queryItems = queryItems

         guard let manifestUrl = urlComponents.url else {
+            if let observer = timeoutObserver {
+                NotificationCenter.default.removeObserver(observer)
+            }
             completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
             return
         }
📝 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.

Suggested change
guard let baseUrl = URL(string: self.releaseConfigURL),
var urlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: false) else {
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}
var queryItems = urlComponents.queryItems ?? []
if !queryItems.contains(where: { $0.name == "extended" }) {
queryItems.append(URLQueryItem(name: "extended", value: "true"))
}
urlComponents.queryItems = queryItems
guard let manifestUrl = urlComponents.url else {
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}
guard let baseUrl = URL(string: self.releaseConfigURL),
var urlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: false) else {
if let observer = timeoutObserver {
NotificationCenter.default.removeObserver(observer)
}
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}
var queryItems = urlComponents.queryItems ?? []
if !queryItems.contains(where: { $0.name == "extended" }) {
queryItems.append(URLQueryItem(name: "extended", value: "true"))
}
urlComponents.queryItems = queryItems
guard let manifestUrl = urlComponents.url else {
if let observer = timeoutObserver {
NotificationCenter.default.removeObserver(observer)
}
completionHandler(nil, NSError(domain: "in.juspay.Airborne", code: 2, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), false)
return
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift`
around lines 1059 - 1075, Remove the registered timeoutObserver before returning
from both URL-validation guard failures in the release-config download flow.
Ensure each failure path deregisters the observer before invoking
completionHandler, preventing the timeout callback from invoking
completionHandler again.

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift`:
- Line 932: Persist unresolved properties in the late-success response path that
calls utils.saveManifestToTemp(manifest), not only in the startDownload
completion path invoking updateUnresolvedProperties. Ensure the standalone
unresolved-properties cache is updated from the successfully received manifest
before or alongside saving the temp manifest, and add a regression test covering
a timed-out request that later succeeds and verifies the persisted values.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1633e0d1-a877-4360-900e-459d926ab4d0

📥 Commits

Reviewing files that changed from the base of the PR and between bc70969 and 9406546.

📒 Files selected for processing (7)
  • airborne_docs/docs/react-native-sdk/reference/callbacks-and-events.md
  • airborne_docs/docs/react-native-sdk/reference/ios-api.md
  • airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift
  • airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwiftCore/AJPFileUtil.swift
  • airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwiftModel/AJPApplicationConstants.swift
  • airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwiftModel/AJPApplicationManifest.swift
  • airborne_sdk_iOS/hyper-ota/AirborneTestAppTests/AJPReleaseConfigExtendedTests.swift

self.downloadedApplicationManifest = manifest
self.releaseConfigDownloadStatus = .completed
self.cleanUpUnwantedFiles()
self.updateUnresolvedProperties(manifest?.unresolvedProperties)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Persist unresolved properties when a timed-out request later succeeds.

Line 932 only updates app-unresolved-properties.dat when the fetch invokes startDownload completion. A valid response received after the timeout takes the utils.saveManifestToTemp(manifest) path instead. initializeDefaults() does not read that temp manifest for unresolved properties. The next launch can therefore return stale or missing data despite the completed response.

Update the standalone cache when saving the late manifest. Add a regression test for this timeout path.

Proposed fix
                 } else {
                     if let manifest = manifest, manifestError == nil {
                         self.tracker.trackInfo("release_config_fetch_after_timeout", value: NSMutableDictionary(dictionary: ["version": manifest.config.version]))
                         utils.saveManifestToTemp(manifest)
+                        self.updateUnresolvedProperties(manifest.unresolvedProperties)
                     }
                 }
📝 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.

Suggested change
self.updateUnresolvedProperties(manifest?.unresolvedProperties)
} else {
if let manifest = manifest, manifestError == nil {
self.tracker.trackInfo("release_config_fetch_after_timeout", value: NSMutableDictionary(dictionary: ["version": manifest.config.version]))
utils.saveManifestToTemp(manifest)
self.updateUnresolvedProperties(manifest.unresolvedProperties)
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@airborne_sdk_iOS/hyper-ota/Airborne/AirborneSwift/AJPApplicationManager.swift`
at line 932, Persist unresolved properties in the late-success response path
that calls utils.saveManifestToTemp(manifest), not only in the startDownload
completion path invoking updateUnresolvedProperties. Ensure the standalone
unresolved-properties cache is updated from the successfully received manifest
before or alongside saving the temp manifest, and add a regression test covering
a timed-out request that later succeeds and verifies the persisted values.

@yuvrajjsingh0 yuvrajjsingh0 changed the title feat: send extended=true while fetching rc in ios feat: unresolved_configs in Release Config Aug 5, 2026
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