Skip to content

fix: support iOS 26#764

Open
HashEngineering wants to merge 15 commits intomasterfrom
fix/support-iOS-26
Open

fix: support iOS 26#764
HashEngineering wants to merge 15 commits intomasterfrom
fix/support-iOS-26

Conversation

@HashEngineering
Copy link
Copy Markdown
Contributor

@HashEngineering HashEngineering commented Apr 14, 2026

Issue being fixed or feature implemented

What was done?

How Has This Been Tested?

Breaking Changes

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Pay-to-contact flow from contacts.
    • Local Currency picker: searchable list and selectable rows.
    • New SwiftUI helpers: scroll/geometry readers and a reusable search bar.
  • UI/UX Improvements

    • Redesigned payment tab visual and interaction.
    • Adjusted shortcuts vertical spacing and collection inset.
    • Refined navigation back button layout.
    • Removed an unused/hidden navigation bar button.
  • Bug Fixes

    • Improved biometric/fallback confirmation messaging.
  • Style

    • Added color tokens for search/background and overlays.

@HashEngineering HashEngineering self-assigned this Apr 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Warning

Rate limit exceeded

@romchornyi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 45 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 17 minutes and 45 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: 9c426c60-2570-4672-af12-120c231c09fa

📥 Commits

Reviewing files that changed from the base of the PR and between a74a184 and b12f1c9.

📒 Files selected for processing (1)
  • .github/workflows/release-xcode26.yml
📝 Walkthrough

Walkthrough

Replaces the payment tab UI with a modal flow, integrates DashPay contact-based payment selection, adds a LocalCurrency SwiftUI feature and several SwiftUI utilities (frame/location readers, scroll view with scroll callbacks, search bar), updates color assets, tweaks navigation/back button layout and security fallback, and updates Xcode project/scheme and CI lanes.

Changes

Cohort / File(s) Summary
Payment Tab State & Flow
DashWallet/Sources/UI/Main/MainTabbarController.swift
Removed custom paymentButton UI; added paymentIsOpened state; generate payment tab image via makePaymentTabImage(); intercept payment tab selection in tabBarController(_:shouldSelect:) to call showPaymentsController(...) and prevent tab switching.
DashPay Contact Pay Integration
DashWallet/Sources/UI/Home/HomeViewController+Shortcuts.swift
Under #if DASHPAY initialize DWContactsViewController with payModel/dataProvider, set intent = .payToSelector, and add DWContactsViewControllerPayDelegate conformance to dismiss contacts UI and call performPay(toUser:).
Shortcuts & Navigation Layout
DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift, DashWallet/Sources/UI/Buy Sell/BuySellPortal.storyboard, DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
Introduced verticalPadding and updated collection view height/insets; removed a disabled right barButtonItem from the BuySellPortal navigation item; removed explicit backButton.imageEdgeInsets on the custom back button.
Local Currency (SwiftUI) Feature
DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift, LocalCurrencyViewModel.swift, CurrencyItem.swift, Cells/LocalCurrencyCellView.swift
Added LocalCurrency SwiftUI screen, view model with debounced search and selection syncing to App.shared.fiatCurrency, CurrencyItem model, and LocalCurrencyCellView with highlighting and selection UI.
SwiftUI Utilities & Components
DashWallet/Sources/UI/SwiftUI Components/Geometry Readers/FrameReader.swift, .../LocationReader.swift, .../ScrollViews/ScrollViewWithOnScrollChanged.swift, SearchBar.swift, Color+DWStyle.swift
Added FrameReader, LocationReader, ScrollViewWithOnScrollChanged, SearchBar components and readingFrame/readingLocation extensions; added color tokens searchBg, black1000Alpha30, black1000Alpha50.
SettingsScreen: SwiftUI Presentation
DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift
Replaced delegate-based DWLocalCurrencyViewController flow with a UIHostingController hosting LocalCurrencyView; wired onSelect/onBack closures to pop navigation.
Security Model Fallback
DashWallet/Sources/UI/Menu/Security/Advanced Security/Model/DWBaseAdvancedSecurityModel.m
Ensure spending confirmation description is always initialized when limit > 0; fallback to localized “biometrics” text instead of asserting if Touch/Face ID unavailable.
Navigation Back Button Layout
DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
Removed explicit negative imageEdgeInsets from dynamically created custom back button; retains same image/tint/target.
Storyboard Tweak
DashWallet/Sources/UI/Buy Sell/BuySellPortal.storyboard
Removed a disabled, fully-transparent right barButtonItem from the Buy Sell Portal scene navigationItem.
Project, Scheme & CI
DashWallet.xcodeproj/project.pbxproj, DashWallet.xcodeproj/xcshareddata/xcschemes/dashwallet.xcscheme, .github/workflows/release-xcode26.yml, fastlane/Fastfile, DashWallet/AppDelegate.m
Updated project objectVersion and added new Swift files/resources to project; changed scheme LaunchAction to Debug; added Xcode 26 CI workflow; added ensure_xcode_26 and release_archive_ci fastlane lanes; added early-return in AppDelegate for Xcode Previews under #if DEBUG.
Assets: Colors
Shared/Resources/SharedAssets.xcassets/.../*.colorset/Contents.json
Added SearchBackground, Black1000Alpha30, Black1000Alpha50 asset definitions and normalized/rewrote several color JSON files (minor formatting/hex conversions).

Sequence Diagram

sequenceDiagram
    actor User
    participant TabBar as UITabBarController
    participant Tab as PaymentTab(EmptyController)
    participant PaymentsUI as PaymentsController

    User->>TabBar: taps payment tab
    TabBar->>TabBar: tabBarController(_:shouldSelect:)
    TabBar-->>PaymentsUI: showPaymentsController(withActivePage:.none)
    PaymentsUI-->>User: present payments UI
    Note over TabBar,PaymentsUI: shouldSelect returns false to prevent tab switch
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hopped through tabs and SwiftUI glows so bright,
Replaced a button with a boolean light,
Contacts now point where payments find their way,
Flags and colors flutter in tidy array,
A little rabbit cheers the UI’s new day!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'fix: support iOS 26' is vague and does not accurately reflect the actual changeset, which involves numerous UI improvements, refactoring, new SwiftUI components, and CI/build configuration updates unrelated to iOS 26 support. Revise the title to accurately describe the main changes, such as 'refactor: replace UIKit payment button with tabbar state and add LocalCurrency SwiftUI view' or 'chore: update Xcode 26 support with UI improvements and new SwiftUI components'.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 fix/support-iOS-26

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
DashWallet/Sources/UI/Main/MainTabbarController.swift (1)

243-254: Fix unused closure parameter and consider consistent error handling.

The static analyzer flags that context is unused. Also, if "tabbar_pay_button" asset is missing, this silently renders just a blue circle (unlike lines 48/67 which force-unwrap).

♻️ Proposed fix
-        let image = renderer.image { context in
+        let image = renderer.image { _ in
             // Draw blue circle background
             UIColor.dw_dashBlue().setFill()
             UIBezierPath(ovalIn: rect).fill()

             // Draw icon centered
-            if let icon = UIImage(named: "tabbar_pay_button") {
+            guard let icon = UIImage(named: "tabbar_pay_button") else {
+                assertionFailure("Missing tabbar_pay_button asset")
+                return
+            }
-                let iconSize = CGSize(width: 22, height: 22)
-                let iconOrigin = CGPoint(x: (size - iconSize.width) / 2, y: (size - iconSize.height) / 2)
-                icon.draw(in: CGRect(origin: iconOrigin, size: iconSize))
-            }
+            let iconSize = CGSize(width: 22, height: 22)
+            let iconOrigin = CGPoint(x: (size - iconSize.width) / 2, y: (size - iconSize.height) / 2)
+            icon.draw(in: CGRect(origin: iconOrigin, size: iconSize))
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Main/MainTabbarController.swift` around lines 243 -
254, In MainTabbarController inside the renderer.image closure, replace the
unused closure parameter `context` with `_` (e.g., `renderer.image { _ in ...
}`) to silence the analyzer, and make handling of the "tabbar_pay_button" asset
consistent with other sites (lines 48/67) by forcing the asset presence instead
of silently skipping: replace the `if let icon = UIImage(named:
"tabbar_pay_button") { ... }` with a non-optional access (or a guard that
aborts/asserts) so the code either draws the icon or fails loudly when the asset
is missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DashWallet/Sources/UI/Home/HomeViewController`+Shortcuts.swift:
- Line 325: The file HomeViewController+Shortcuts.swift is missing a single
trailing newline at EOF which triggers SwiftLint; open the file
(HomeViewController+Shortcuts.swift) and ensure the file ends with exactly one
newline character (add a single trailing newline after the last line) so it
conforms to the single trailing newline rule used by SwiftLint/SwiftFormat.

In `@DashWallet/Sources/UI/Main/MainTabbarController.swift`:
- Line 93: Remove the dead stored property paymentIsOpened from
MainTabbarController: delete the declaration `private var paymentIsOpened =
false` and remove any assignments to it in closePayments(),
paymentsViewControllerWantsToImportPrivateKey(), and showPaymentsController();
keep the existing flow implemented by the EmptyController
interceptor/PaymentButton refactor and ensure no other code references
paymentIsOpened (search for the symbol and remove or adapt any leftover uses).

---

Nitpick comments:
In `@DashWallet/Sources/UI/Main/MainTabbarController.swift`:
- Around line 243-254: In MainTabbarController inside the renderer.image
closure, replace the unused closure parameter `context` with `_` (e.g.,
`renderer.image { _ in ... }`) to silence the analyzer, and make handling of the
"tabbar_pay_button" asset consistent with other sites (lines 48/67) by forcing
the asset presence instead of silently skipping: replace the `if let icon =
UIImage(named: "tabbar_pay_button") { ... }` with a non-optional access (or a
guard that aborts/asserts) so the code either draws the icon or fails loudly
when the asset is missing.
🪄 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: 726a1ba0-1c64-45d6-9516-323bce902d09

📥 Commits

Reviewing files that changed from the base of the PR and between bce2e51 and f870628.

⛔ Files ignored due to path filters (1)
  • DashWallet/Resources/AppAssets.xcassets/Shortcuts/shortcut_scanToPay.imageset/scan.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • DashWallet/Sources/UI/Buy Sell/BuySellPortal.storyboard
  • DashWallet/Sources/UI/Home/HomeViewController+Shortcuts.swift
  • DashWallet/Sources/UI/Home/Views/Shortcuts/ShortcutsView.swift
  • DashWallet/Sources/UI/Main/MainTabbarController.swift
  • DashWallet/Sources/UI/Menu/Security/Advanced Security/Model/DWBaseAdvancedSecurityModel.m
  • DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
💤 Files with no reviewable changes (2)
  • DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
  • DashWallet/Sources/UI/Buy Sell/BuySellPortal.storyboard

}
}
#endif

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 | 🟡 Minor

Fix trailing newline lint warning.

SwiftLint reports the file ending does not satisfy the single trailing newline rule.

✂️ Minimal formatting fix
-#endif
-
+#endif

As per coding guidelines: **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

📝 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
`#endif`
🧰 Tools
🪛 SwiftLint (0.63.2)

[Warning] 325-325: Files should have a single trailing newline

(trailing_newline)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Home/HomeViewController`+Shortcuts.swift at line 325,
The file HomeViewController+Shortcuts.swift is missing a single trailing newline
at EOF which triggers SwiftLint; open the file
(HomeViewController+Shortcuts.swift) and ensure the file ends with exactly one
newline character (add a single trailing newline after the last line) so it
conforms to the single trailing newline rule used by SwiftLint/SwiftFormat.

weak var wipeDelegate: DWWipeDelegate?

private var paymentButton: PaymentButton!
private var paymentIsOpened = false
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if paymentIsOpened is read anywhere in the codebase
rg -n 'paymentIsOpened' --type swift -C2

Repository: dashpay/dashwallet-ios

Length of output: 1829


Remove the unused paymentIsOpened property.

This property is assigned in closePayments(), paymentsViewControllerWantsToImportPrivateKey(), and showPaymentsController() but never read. It appears to be dead code from the refactoring that replaced PaymentButton with the EmptyController interceptor pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Main/MainTabbarController.swift` at line 93, Remove the
dead stored property paymentIsOpened from MainTabbarController: delete the
declaration `private var paymentIsOpened = false` and remove any assignments to
it in closePayments(), paymentsViewControllerWantsToImportPrivateKey(), and
showPaymentsController(); keep the existing flow implemented by the
EmptyController interceptor/PaymentButton refactor and ensure no other code
references paymentIsOpened (search for the symbol and remove or adapt any
leftover uses).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (7)
DashWallet/Sources/UI/Menu/Settings/LocalCurrency/Cells/LocalCurrencyCellView.swift (2)

45-59: Use explicit shapes if older iOS targets are still supported.

Lines 45 and 59 use SwiftUI shape shorthand. If the deployment target is below iOS 17, replace it with explicit shapes to avoid availability issues. Apple Shape docs list these as standard-shape shorthand APIs: https://developer.apple.com/documentation/SwiftUI/Shape

♻️ Proposed compatibility-safe change
-        .clipShape(.rect)
+        .clipped()
-        .clipShape(.circle)
+        .clipShape(Circle())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/Cells/LocalCurrencyCellView.swift`
around lines 45 - 59, The shorthand shape usage in flagSection (the
.clipShape(.rect) and .clipShape(.circle) calls) can break on pre-iOS17 targets;
replace those shorthand calls with explicit shape types (e.g.,
clipShape(Rectangle()) and clipShape(Circle())) in the LocalCurrencyCellView's
flagSection, keeping the frame size (Layout.flagSize) and the UIImage(named:)
existence check logic unchanged.

101-113: Prefer Text concatenation for highlighted ranges.

This avoids relying on localized-string interpolation for dynamic currency names/codes and keeps the styled segment explicit.

♻️ Proposed simplification
-        return Text("\(before)\(Text(match).foregroundColor(.dashBlue))\(after)")
+        return Text(before) + Text(match).foregroundColor(.dashBlue) + Text(after)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/Cells/LocalCurrencyCellView.swift`
around lines 101 - 113, The highlightedText(_ text: String, query: String)
function currently builds a single interpolated Text using
"\(before)\(Text(match).foregroundColor(.dashBlue))\(after)" which can trigger
localized-string interpolation and is less explicit; change it to concatenate
explicit Text pieces instead: create Text(before) +
Text(match).foregroundColor(.dashBlue) + Text(after) so the styled segment is an
explicit Text view and avoids interpolation/localization issues while preserving
the existing trimming/range logic.
DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift (2)

48-48: Avoid iOS 17 shape shorthand in a pre-iOS 17-compatible view.

Line 48 uses .rect(cornerRadius:) while this same component has an earlier-iOS fallback at Lines 118-125. If the app still supports iOS 16 or below, prefer the explicit shape initializer. Apple Shape docs list .rect as SwiftUI standard-shape shorthand: https://developer.apple.com/documentation/SwiftUI/Shape

♻️ Proposed compatibility-safe change
-            .clipShape(.rect(cornerRadius: Layout.fieldCornerRadius))
+            .clipShape(RoundedRectangle(cornerRadius: Layout.fieldCornerRadius, style: .continuous))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift at line 48, The
view uses the iOS 17 shape shorthand `.rect(cornerRadius:)` in the clipShape
call; replace that shorthand with the explicit shape initializer used in the
earlier fallback (e.g., use RoundedRectangle(cornerRadius:
Layout.fieldCornerRadius) as the argument to clipShape) so the SearchBar.swift
component remains compatible with iOS 16 and earlier; update the clipShape call
where `.rect(cornerRadius: Layout.fieldCornerRadius)` appears (and ensure visual
parity with the existing fallback logic around the earlier lines).

35-35: Remove or wire up the unused cancel-button measurement state.

cancelButtonWidth and cancelButtonMeasurement are never read by body, so this adds dead state updates without affecting layout.

🧹 Proposed cleanup
-    `@State` private var cancelButtonWidth: CGFloat = 0
-    private var cancelButtonMeasurement: some View {
-        cancelButton
-            .fixedSize()
-            .padding(.leading, Layout.fieldSpacing)
-            .hidden()
-            .captureSize { size in
-                if abs(cancelButtonWidth - size.width) > 0.5 {
-                    cancelButtonWidth = size.width
-                }
-            }
-    }
-

Also applies to: 91-101

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift at line 35, The
cancelButtonWidth and cancelButtonMeasurement `@State` properties in SearchBar are
unused by body and should be removed to eliminate dead state; find the
declarations cancelButtonWidth and cancelButtonMeasurement (and any related
measurement helper code that only updates them) and either wire them into layout
logic used by body (e.g., use cancelButtonWidth to conditionally animate or
offset the cancel button) or simply delete both state properties and their
update calls so no unused state updates remain.
DashWallet/Sources/UI/SwiftUI Components/Geometry Readers/FrameReader.swift (1)

20-24: Clean up the SwiftLint warnings in this new utility.

Move the doc comment before the availability attribute, use Void, and discard the unused preview loop parameter.

🧹 Proposed SwiftLint cleanup
-@available(iOS 14, *)
 /// Adds a transparent View and read it's frame.
 ///
 /// Adds a GeometryReader with infinity frame.
+@available(iOS 14, *)
 public struct FrameReader: View {
@@
-    func readingFrame(coordinateSpace: CoordinateSpace = .global, onChange: `@escaping` (_ frame: CGRect) -> ()) -> some View {
+    func readingFrame(coordinateSpace: CoordinateSpace = .global, onChange: `@escaping` (_ frame: CGRect) -> Void) -> some View {
@@
-                    ForEach(0..<30) { x in
+                    ForEach(0..<30) { _ in

As per coding guidelines, **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

Also applies to: 53-53, 78-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/Geometry Readers/FrameReader.swift
around lines 20 - 24, Move the doc comment for FrameReader to sit before the
`@available`(iOS 14, *) attribute, update any functions or type aliases returning
an empty tuple to use Void, and remove or replace the unused preview loop
parameter in the PreviewProvider (discard it with _ or remove the loop variable)
so SwiftLint warnings are resolved; check the FrameReader struct and its
associated preview code (including the occurrences referenced around the other
instances) to apply these three changes consistently.
DashWallet/Sources/UI/SwiftUI Components/ScrollViews/ScrollViewWithOnScrollChanged.swift (1)

21-33: Apply the SwiftLint spelling for the generic constraint and closure return type.

This keeps the new component compliant with the configured lint rules.

🧹 Proposed SwiftLint cleanup
-public struct ScrollViewWithOnScrollChanged<Content:View>: View {
+public struct ScrollViewWithOnScrollChanged<Content: View>: View {
@@
-    let onScrollChanged: (_ origin: CGPoint) -> ()
+    let onScrollChanged: (_ origin: CGPoint) -> Void
@@
-        onScrollChanged: `@escaping` (_ origin: CGPoint) -> ()) {
+        onScrollChanged: `@escaping` (_ origin: CGPoint) -> Void) {

As per coding guidelines, **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI`
Components/ScrollViews/ScrollViewWithOnScrollChanged.swift around lines 21 - 33,
The generic constraint and closure return type in ScrollViewWithOnScrollChanged
should follow SwiftLint rules: change the generic constraint in the struct
declaration (ScrollViewWithOnScrollChanged<Content:View>) to include a space
after the colon (ScrollViewWithOnScrollChanged<Content: View>) and update the
onScrollChanged closure signature (onScrollChanged: (_ origin: CGPoint) -> ())
to use Swift's Void type (onScrollChanged: `@escaping` (_ origin: CGPoint) ->
Void); apply the same Void return type in the public init parameter list so both
the stored property and initializer use the corrected closure signature.
DashWallet/Sources/UI/SwiftUI Components/Geometry Readers/LocationReader.swift (1)

20-24: Clean up the SwiftLint warnings in the location reader.

Move the doc comment before the availability attribute, use Void, and replace the unused preview parameter with _.

🧹 Proposed SwiftLint cleanup
-@available(iOS 14, *)
 /// Adds a transparent View and read it's center point.
 ///
 /// Adds a GeometryReader with 0px by 0px frame.
+@available(iOS 14, *)
 public struct LocationReader: View {
@@
-    func readingLocation(coordinateSpace: CoordinateSpace = .global, onChange: `@escaping` (_ location: CGPoint) -> ()) -> some View {
+    func readingLocation(coordinateSpace: CoordinateSpace = .global, onChange: `@escaping` (_ location: CGPoint) -> Void) -> some View {
@@
-                    ForEach(0..<30) { x in
+                    ForEach(0..<30) { _ in

As per coding guidelines, **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

Also applies to: 48-48, 74-74

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/Geometry
Readers/LocationReader.swift around lines 20 - 24, Move the doc comment so it
appears before the `@available`(iOS 14, *) attribute for the LocationReader
struct, change any function or property type using the empty tuple () to Void,
and replace the named unused preview parameter in the preview provider (or any
initializer/closure) with an underscore (_) to silence unused-parameter
SwiftLint warnings; update occurrences inside the LocationReader type and its
PreviewProvider to reflect these changes (look for symbols LocationReader and
the preview provider or preview/init parameter name).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DashWallet.xcodeproj/project.pbxproj`:
- Line 6: The project contains objectVersion = 70 and Xcode 16-specific
PBXFileSystemSynchronizedRootGroup entries which require a newer xcodeproj gem;
update the Gemfile to require xcodeproj >= 1.27.0 (and run bundle update
xcodeproj to refresh Gemfile.lock) so CocoaPods/Xcode project parsing succeeds,
and update CI configuration (the workflow or image settings) to use Xcode 16+ so
the environment matches the project format.
- Around line 497-501: The PBX project contains duplicate PBXBuildFile entries
for several Info.plist resources in the same PBXResourcesBuildPhase; locate the
duplicated PBXBuildFile entries (e.g., the two Topper-Info.plist entries
5151C8C92F913BF100F0A604 and 5151C8CD2F913BF100F0A604, the duplicated
Coinbase-Info.plist, GoogleService-Info.plist, and ZenLedger-Info.plist entries)
and remove the redundant PBXBuildFile lines from each PBXResourcesBuildPhase
(for phases 75D5F3BC191EC270004AB296 and C9D2C90A2A320AA000D15901), keeping only
the canonical PBXBuildFile reference (the correct fileRef) for each plist so
each resource appears exactly once in its Resources build phase.

In `@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift`:
- Line 107: The SwiftLint complaints in LocalCurrencyView relate to spacing and
ternary usage in the .padding call and an operator-spacing violation elsewhere;
update the .padding(filteredItems.count > 0 ? 6 : 0) usage in LocalCurrencyView
to a lint-friendly form (for example use filteredItems.isEmpty ? 0 : 6 or ensure
proper spaces around operators) so the ternary and comparison follow SwiftLint
rules, and fix the operator spacing at the other offending expression (line with
the operator violation) by adding/removing spaces so it conforms to
operator_usage_whitespace; then run SwiftFormat/SwiftLint to reformat and verify
no warnings remain.
- Line 136: The .animation(.smooth, value: scrollOffset) call in
LocalCurrencyView uses Animation.smooth which is iOS 17-only and will crash on
iOS 15–16; replace it with a cross-version animation (e.g., .animation(.default,
value: scrollOffset) or .animation(.interactiveSpring(...), value: scrollOffset)
) or wrap the iOS-17-only animation in an availability check so that for iOS 17+
you use .smooth and for earlier OSes you fall back to a supported animation,
keeping the reference to scrollOffset and LocalCurrencyView unchanged.

In
`@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyViewModel.swift`:
- Line 125: The entry in the flagByCode dictionary uses "Algeria" with capital A
which breaks the lowercase asset naming convention; update the value for key
"DZD" in flagByCode to "algeria" so it matches the other lowercase flag asset
names and the expected asset lookup behavior.

In `@DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift`:
- Around line 189-201: The showCurrencySelector function currently pushes a raw
UIHostingController(rootView: LocalCurrencyView) which bypasses
BaseNavigationController's navigation/back handling; create a thin hosting
controller subclass (e.g., LocalCurrencyHostingController:
UIHostingController<LocalCurrencyView>) that conforms to the same
navigation-display protocol or interfaces used by other SwiftUI wrappers in the
project, instantiate that wrapper with the LocalCurrencyView, and push the
wrapper (controller) instead of a bare UIHostingController in
showCurrencySelector to restore proper nav-bar/back-button behavior.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift:
- Around line 66-83: Remove the unnecessary `@ViewBuilder` annotation from the
clearButton computed property and convert explicit Button(action: { ... }) { ...
} forms to the preferred trailing-label style using the closure-first syntax,
e.g. change Button(action: { text = "" }) { Image(...) } to Button { text = "" }
label: { Image(systemName: "xmark.circle.fill").foregroundColor(...) }, and do
the same refactor for cancelButton (change Button(action: { text = "";
withAnimation { isEditing = false }; isFocused = false }) { ... } to Button {
text = ""; withAnimation(.easeInOut(duration: Layout.animationDuration)) {
isEditing = false }; isFocused = false } label: { ... }), ensuring you update
the label closures accordingly.
- Line 29: Change the static constant animationDuration from CGFloat to
TimeInterval (or Double) in the SearchBar component so it matches SwiftUI's
Animation.easeInOut(duration:), and update any places referencing
SearchBar.animationDuration (used in the animation modifiers around the view
state changes) to pass the value directly without casting; ensure the constant's
declaration (animationDuration) and all usages in animation(...) calls use the
TimeInterval/Double type.

---

Nitpick comments:
In
`@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/Cells/LocalCurrencyCellView.swift`:
- Around line 45-59: The shorthand shape usage in flagSection (the
.clipShape(.rect) and .clipShape(.circle) calls) can break on pre-iOS17 targets;
replace those shorthand calls with explicit shape types (e.g.,
clipShape(Rectangle()) and clipShape(Circle())) in the LocalCurrencyCellView's
flagSection, keeping the frame size (Layout.flagSize) and the UIImage(named:)
existence check logic unchanged.
- Around line 101-113: The highlightedText(_ text: String, query: String)
function currently builds a single interpolated Text using
"\(before)\(Text(match).foregroundColor(.dashBlue))\(after)" which can trigger
localized-string interpolation and is less explicit; change it to concatenate
explicit Text pieces instead: create Text(before) +
Text(match).foregroundColor(.dashBlue) + Text(after) so the styled segment is an
explicit Text view and avoids interpolation/localization issues while preserving
the existing trimming/range logic.

In `@DashWallet/Sources/UI/SwiftUI` Components/Geometry Readers/FrameReader.swift:
- Around line 20-24: Move the doc comment for FrameReader to sit before the
`@available`(iOS 14, *) attribute, update any functions or type aliases returning
an empty tuple to use Void, and remove or replace the unused preview loop
parameter in the PreviewProvider (discard it with _ or remove the loop variable)
so SwiftLint warnings are resolved; check the FrameReader struct and its
associated preview code (including the occurrences referenced around the other
instances) to apply these three changes consistently.

In `@DashWallet/Sources/UI/SwiftUI` Components/Geometry
Readers/LocationReader.swift:
- Around line 20-24: Move the doc comment so it appears before the
`@available`(iOS 14, *) attribute for the LocationReader struct, change any
function or property type using the empty tuple () to Void, and replace the
named unused preview parameter in the preview provider (or any
initializer/closure) with an underscore (_) to silence unused-parameter
SwiftLint warnings; update occurrences inside the LocationReader type and its
PreviewProvider to reflect these changes (look for symbols LocationReader and
the preview provider or preview/init parameter name).

In `@DashWallet/Sources/UI/SwiftUI`
Components/ScrollViews/ScrollViewWithOnScrollChanged.swift:
- Around line 21-33: The generic constraint and closure return type in
ScrollViewWithOnScrollChanged should follow SwiftLint rules: change the generic
constraint in the struct declaration
(ScrollViewWithOnScrollChanged<Content:View>) to include a space after the colon
(ScrollViewWithOnScrollChanged<Content: View>) and update the onScrollChanged
closure signature (onScrollChanged: (_ origin: CGPoint) -> ()) to use Swift's
Void type (onScrollChanged: `@escaping` (_ origin: CGPoint) -> Void); apply the
same Void return type in the public init parameter list so both the stored
property and initializer use the corrected closure signature.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift:
- Line 48: The view uses the iOS 17 shape shorthand `.rect(cornerRadius:)` in
the clipShape call; replace that shorthand with the explicit shape initializer
used in the earlier fallback (e.g., use RoundedRectangle(cornerRadius:
Layout.fieldCornerRadius) as the argument to clipShape) so the SearchBar.swift
component remains compatible with iOS 16 and earlier; update the clipShape call
where `.rect(cornerRadius: Layout.fieldCornerRadius)` appears (and ensure visual
parity with the existing fallback logic around the earlier lines).
- Line 35: The cancelButtonWidth and cancelButtonMeasurement `@State` properties
in SearchBar are unused by body and should be removed to eliminate dead state;
find the declarations cancelButtonWidth and cancelButtonMeasurement (and any
related measurement helper code that only updates them) and either wire them
into layout logic used by body (e.g., use cancelButtonWidth to conditionally
animate or offset the cancel button) or simply delete both state properties and
their update calls so no unused state updates remain.
🪄 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: 0dfcfd9f-8697-4aab-987f-1552d360eb2b

📥 Commits

Reviewing files that changed from the base of the PR and between f870628 and c57de5e.

📒 Files selected for processing (19)
  • DashWallet.xcodeproj/project.pbxproj
  • DashWallet.xcodeproj/xcshareddata/xcschemes/dashwallet.xcscheme
  • DashWallet/AppDelegate.m
  • DashWallet/Sources/UI/Menu/Settings/LocalCurrency/Cells/LocalCurrencyCellView.swift
  • DashWallet/Sources/UI/Menu/Settings/LocalCurrency/CurrencyItem.swift
  • DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift
  • DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyViewModel.swift
  • DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift
  • DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift
  • DashWallet/Sources/UI/SwiftUI Components/Geometry Readers/FrameReader.swift
  • DashWallet/Sources/UI/SwiftUI Components/Geometry Readers/LocationReader.swift
  • DashWallet/Sources/UI/SwiftUI Components/ScrollViews/ScrollViewWithOnScrollChanged.swift
  • DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift
  • Shared/Resources/SharedAssets.xcassets/Colors/Black/Black1000Alpha30.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/Black/Black1000Alpha50.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/NumberKeyboardHighlightedTextColor.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/SearchBackground.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/ShadowColor.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/TertiaryBackgroundColor.colorset/Contents.json
✅ Files skipped from review due to trivial changes (9)
  • DashWallet.xcodeproj/xcshareddata/xcschemes/dashwallet.xcscheme
  • DashWallet/AppDelegate.m
  • Shared/Resources/SharedAssets.xcassets/Colors/TertiaryBackgroundColor.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/Black/Black1000Alpha50.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/NumberKeyboardHighlightedTextColor.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/ShadowColor.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/Black/Black1000Alpha30.colorset/Contents.json
  • Shared/Resources/SharedAssets.xcassets/Colors/SearchBackground.colorset/Contents.json
  • DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift

classes = {
};
objectVersion = 54;
objectVersion = 70;
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the project format features introduced by this change.
python3 - <<'PY'
from pathlib import Path
p = Path("DashWallet.xcodeproj/project.pbxproj")
text = p.read_text()
for line in text.splitlines():
    if "objectVersion" in line:
        print(line.strip())
print("PBXFileSystemSynchronizedRootGroup present:", "PBXFileSystemSynchronizedRootGroup" in text)
print("fileSystemSynchronizedGroups present:", "fileSystemSynchronizedGroups" in text)
PY

# Check whether Ruby/CocoaPods tooling is pinned and therefore needs an update.
fd -i '^(Gemfile|Gemfile\.lock|Podfile\.lock)$' -x sh -c '
  printf "\n--- %s ---\n" "$1"
  sed -n "/cocoapods/Ip;/xcodeproj/Ip;/Xcode/Ip" "$1"
' sh {}

Repository: dashpay/dashwallet-ios

Length of output: 1076


🌐 Web query:

xcodeproj 1.22.0 objectVersion 70 compatibility

💡 Result:

xcodeproj version 1.22.0 does not support objectVersion 70. This version, released in 2022, added support up to Xcode 14.0 (objectVersion 56). objectVersion 70 corresponds to Xcode 16.0 and was added in later versions of xcodeproj (around 1.27+ or via PR #1007). Using 1.22.0 with projects having objectVersion 70 will raise ArgumentError: Unable to find compatibility version string for object version 70. Workarounds include updating xcodeproj gem or manually changing the project's objectVersion to 56 or lower in project.pbxproj, or setting Project Format to an older Xcode version in Xcode.

Citations:


🌐 Web query:

PBXFileSystemSynchronizedRootGroup xcodeproj minimum version support

💡 Result:

PBXFileSystemSynchronizedRootGroup is a new project object type (ISA) introduced in Xcode 16 for file system synchronized root groups, which allow targets to reference folders with automatic file synchronization to reduce Git conflicts. It first appears in Xcode projects created or modified with Xcode 16 (released September 16, 2024), associated with project objectVersion 77 and preferredProjectObjectVersion 77. Xcode versions prior to 16 do not support this type and cannot open projects containing it without modification (e.g., converting to PBXGroup or downgrading objectVersion), resulting in errors like "unknown ISA PBXFileSystemSynchronizedRootGroup" or "future Xcode project file format". Tools like CocoaPods/Xcodeproj, Tuist/XcodeProj, Fastlane, and others added support via updates in 2024-2025. As of 2026-04-20, it is natively supported only in Xcode 16 and later.

Citations:


🌐 Web query:

Xcode objectVersion 70 minimum Xcode version requirement

💡 Result:

Xcode objectVersion 70 corresponds to Xcode 16.0 and requires Xcode 16 or later to open projects using this format. Older versions like Xcode 15 cannot parse projects with objectVersion 70, causing crashes or errors.

Citations:


Update xcodeproj gem to 1.27+ to support objectVersion 70.

objectVersion 70 requires xcodeproj 1.27 or later (currently 1.22.0 in Gemfile.lock). CocoaPods will fail with ArgumentError: Unable to find compatibility version string for object version 70 when trying to read or modify the project. Additionally, PBXFileSystemSynchronizedRootGroup is an Xcode 16+ feature that older xcodeproj versions cannot parse. Update Gemfile to require xcodeproj >= 1.27.0 and ensure CI uses Xcode 16 or later.

Applies to: 6, 3234-3237, 7790-7793, 7893-7896

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet.xcodeproj/project.pbxproj` at line 6, The project contains
objectVersion = 70 and Xcode 16-specific PBXFileSystemSynchronizedRootGroup
entries which require a newer xcodeproj gem; update the Gemfile to require
xcodeproj >= 1.27.0 (and run bundle update xcodeproj to refresh Gemfile.lock) so
CocoaPods/Xcode project parsing succeeds, and update CI configuration (the
workflow or image settings) to use Xcode 16+ so the environment matches the
project format.

Comment on lines +497 to +501
5151C8C92F913BF100F0A604 /* Topper-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C42F913BF100F0A604 /* Topper-Info.plist */; };
5151C8CA2F913BF100F0A604 /* Coinbase-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C22F913BF100F0A604 /* Coinbase-Info.plist */; };
5151C8CB2F913BF100F0A604 /* ZenLedger-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C52F913BF100F0A604 /* ZenLedger-Info.plist */; };
5151C8CC2F913BF100F0A604 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C32F913BF100F0A604 /* GoogleService-Info.plist */; };
5151C8CD2F913BF100F0A604 /* Topper-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C42F913BF100F0A604 /* Topper-Info.plist */; };
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re
from collections import Counter

text = Path("DashWallet.xcodeproj/project.pbxproj").read_text()

phase_re = re.compile(
    r'([A-F0-9]{24}) /\* ([^*]+) \*/ = \{\s*isa = PBXResourcesBuildPhase;.*?files = \((.*?)\);',
    re.S
)

found = False
for phase_id, phase_name, files_block in phase_re.findall(text):
    names = re.findall(r'/\* ([^*]+?) in Resources \*/', files_block)
    duplicates = {name: count for name, count in Counter(names).items() if count > 1}
    if duplicates:
        found = True
        print(f"{phase_name} ({phase_id}) duplicate resources:")
        for name, count in sorted(duplicates.items()):
            print(f"  {name}: {count}")

if not found:
    print("No duplicate resource display names found in PBXResourcesBuildPhase entries.")
PY

Repository: dashpay/dashwallet-ios

Length of output: 341


Remove duplicate resource entries that appear multiple times in the same build phase.

Verification found duplicate display names across PBXResourcesBuildPhase entries:

  • In Resources phase 75D5F3BC191EC270004AB296: Topper-Info.plist (2×), Coinbase-Info.plist (2×), GoogleService-Info.plist (2×), ZenLedger-Info.plist (2×)
  • In Resources phase C9D2C90A2A320AA000D15901: GoogleService-Info.plist (2×), ZenLedger-Info.plist (2×)

This will cause duplicate bundle outputs or build failures. Remove one entry for each duplicated resource, keeping only the canonical reference.

Example: Proposed cleanup for Topper-Info.plist if existing entry is canonical
-		5151C8CD2F913BF100F0A604 /* Topper-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5151C8C42F913BF100F0A604 /* Topper-Info.plist */; };
-				5151C8CD2F913BF100F0A604 /* Topper-Info.plist in Resources */,
 				75E2F3C82AA4CF1900C3B458 /* Topper-Info.plist in Resources */,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet.xcodeproj/project.pbxproj` around lines 497 - 501, The PBX project
contains duplicate PBXBuildFile entries for several Info.plist resources in the
same PBXResourcesBuildPhase; locate the duplicated PBXBuildFile entries (e.g.,
the two Topper-Info.plist entries 5151C8C92F913BF100F0A604 and
5151C8CD2F913BF100F0A604, the duplicated Coinbase-Info.plist,
GoogleService-Info.plist, and ZenLedger-Info.plist entries) and remove the
redundant PBXBuildFile lines from each PBXResourcesBuildPhase (for phases
75D5F3BC191EC270004AB296 and C9D2C90A2A320AA000D15901), keeping only the
canonical PBXBuildFile reference (the correct fileRef) for each plist so each
resource appears exactly once in its Resources build phase.

}
}
}
.padding(filteredItems.count > 0 ? 6 : 0)
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 | 🟡 Minor

Fix the SwiftLint error and spacing warnings in the changed view.

Line 107 is reported as a SwiftLint error; line 164 also violates operator spacing.

🧹 Proposed SwiftLint cleanup
-                .padding(filteredItems.count > 0 ? 6 : 0)
+                .padding(!filteredItems.isEmpty ? 6 : 0)
@@
-                .background(Color(red: 176/255, green: 182/255, blue: 188/255, opacity: 0.15))
+                .background(Color(red: 176 / 255, green: 182 / 255, blue: 188 / 255, opacity: 0.15))

As per coding guidelines, **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

Also applies to: 164-164

🧰 Tools
🪛 SwiftLint (0.63.2)

[Error] 107-107: Prefer checking isEmpty over comparing count to zero

(empty_count)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift` at
line 107, The SwiftLint complaints in LocalCurrencyView relate to spacing and
ternary usage in the .padding call and an operator-spacing violation elsewhere;
update the .padding(filteredItems.count > 0 ? 6 : 0) usage in LocalCurrencyView
to a lint-friendly form (for example use filteredItems.isEmpty ? 0 : 6 or ensure
proper spaces around operators) so the ternary and comparison follow SwiftLint
rules, and fix the operator spacing at the other offending expression (line with
the operator violation) by adding/removing spaces so it conforms to
operator_usage_whitespace; then run SwiftFormat/SwiftLint to reformat and verify
no warnings remain.

}
.padding(.bottom, 6)
.background(toolbarBackground)
.animation(.smooth, value: scrollOffset)
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect iOS deployment targets and SwiftUI Animation.smooth usages.

echo "Deployment targets:"
rg -n --iglob '*.pbxproj' 'IPHONEOS_DEPLOYMENT_TARGET\s*='

echo
echo "Animation.smooth usages:"
rg -n --type swift '\.animation\(\.smooth\b|Animation\.smooth\b'

Repository: dashpay/dashwallet-ios

Length of output: 2206


🏁 Script executed:

cat -n DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift | sed -n '130,145p'

Repository: dashpay/dashwallet-ios

Length of output: 651


Remove .smooth animation or add availability guard.

Animation.smooth requires iOS 17.0, but the app deployment target is iOS 15.0. This line will fail on iOS 15–16 devices at runtime. Use a widely available animation instead.

Fix
-        .animation(.smooth, value: scrollOffset)
+        .animation(.easeInOut(duration: 0.2), value: scrollOffset)
📝 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
.animation(.smooth, value: scrollOffset)
.animation(.easeInOut(duration: 0.2), value: scrollOffset)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyView.swift` at
line 136, The .animation(.smooth, value: scrollOffset) call in LocalCurrencyView
uses Animation.smooth which is iOS 17-only and will crash on iOS 15–16; replace
it with a cross-version animation (e.g., .animation(.default, value:
scrollOffset) or .animation(.interactiveSpring(...), value: scrollOffset) ) or
wrap the iOS-17-only animation in an availability check so that for iOS 17+ you
use .smooth and for earlier OSes you fall back to a supported animation, keeping
the reference to scrollOffset and LocalCurrencyView unchanged.

"DJF": "djibouti",
"DKK": "denmark",
"DOP": "dominican republic",
"DZD": "Algeria",
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 | 🟡 Minor

Normalize the DZD flag asset name casing.

Every neighboring flagByCode value is lowercase, so "Algeria" is likely to miss the expected lowercase asset name.

🏳️ Proposed fix
-        "DZD": "Algeria",
+        "DZD": "algeria",
📝 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
"DZD": "Algeria",
"DZD": "algeria",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@DashWallet/Sources/UI/Menu/Settings/LocalCurrency/LocalCurrencyViewModel.swift`
at line 125, The entry in the flagByCode dictionary uses "Algeria" with capital
A which breaks the lowercase asset naming convention; update the value for key
"DZD" in flagByCode to "algeria" so it matches the other lowercase flag asset
names and the expected asset lookup behavior.

Comment on lines 189 to 201
private func showCurrencySelector() {
let controller = DWLocalCurrencyViewController(navigationAppearance: .default, presentationMode: .screen, currencyCode: nil)
controller.delegate = delegateInternal
let view = LocalCurrencyView(
currencyCode: nil,
onSelect: { [weak vc] _ in
vc?.popViewController(animated: true)
},
onBack: { [weak vc] in
vc?.popViewController(animated: true)
}
)
let controller = UIHostingController(rootView: view)
controller.hidesBottomBarWhenPushed = true
vc.pushViewController(controller, animated: true)
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

Wrap LocalCurrencyView in a thin hosting controller before pushing.

Line 199 pushes a bare UIHostingController, which can bypass this project’s BaseNavigationController navigation-bar/back-button handling. Use the existing wrapper pattern for pushed SwiftUI screens. Based on learnings, in the DashWallet iOS project, when pushing SwiftUI views into BaseNavigationController, wrap the SwiftUI view in a thin UIViewController subclass instead of pushing a bare UIHostingController.

🧭 Suggested direction
-        let controller = UIHostingController(rootView: view)
+        let controller = LocalCurrencyHostingController(rootView: view)
         controller.hidesBottomBarWhenPushed = true
         vc.pushViewController(controller, animated: true)

Add a thin wrapper matching the existing project pattern/protocols:

private final class LocalCurrencyHostingController: UIHostingController<LocalCurrencyView> {
    // Conform to the same navigation-display protocol used by existing SwiftUI wrappers,
    // if required by BaseNavigationController.
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift` around lines 189 -
201, The showCurrencySelector function currently pushes a raw
UIHostingController(rootView: LocalCurrencyView) which bypasses
BaseNavigationController's navigation/back handling; create a thin hosting
controller subclass (e.g., LocalCurrencyHostingController:
UIHostingController<LocalCurrencyView>) that conforms to the same
navigation-display protocol or interfaces used by other SwiftUI wrappers in the
project, instantiate that wrapper with the LocalCurrencyView, and push the
wrapper (controller) instead of a bare UIHostingController in
showCurrencySelector to restore proper nav-bar/back-button behavior.

static let fieldSpacing: CGFloat = 10
static let cancelHorizontalPadding: CGFloat = 12
static let cancelVerticalPadding: CGFloat = 6
static let animationDuration: CGFloat = 0.25
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 | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Apple SwiftUI Animation.easeInOut(duration:) parameter type TimeInterval

💡 Result:

The duration parameter of SwiftUI.Animation.easeInOut(duration:) is of type TimeInterval. This is explicitly stated in the official Apple Developer Documentation, where the method signature is static func easeInOut(duration: TimeInterval) -> Animation. The documentation describes duration as "The length of time, expressed in seconds, that the animation takes to complete." TimeInterval is a type alias for Double in Foundation, commonly used for time durations in seconds across Apple frameworks.

Citations:


🏁 Script executed:

find . -name "SearchBar.swift" -type f | head -20

Repository: dashpay/dashwallet-ios

Length of output: 124


🏁 Script executed:

cat -n "./DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift" | head -80

Repository: dashpay/dashwallet-ios

Length of output: 3341


🏁 Script executed:

cat -n "./DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift" | tail -20

Repository: dashpay/dashwallet-ios

Length of output: 637


Use Double/TimeInterval for animation duration.

Line 29 stores animationDuration as CGFloat, but SwiftUI's Animation.easeInOut(duration:) expects TimeInterval (a Double alias). This causes a type mismatch when passed to animation modifiers at lines 55, 60, and 79.

🐛 Proposed fix
-        static let animationDuration: CGFloat = 0.25
+        static let animationDuration: Double = 0.25
📝 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
static let animationDuration: CGFloat = 0.25
static let animationDuration: Double = 0.25
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift at line 29, Change
the static constant animationDuration from CGFloat to TimeInterval (or Double)
in the SearchBar component so it matches SwiftUI's
Animation.easeInOut(duration:), and update any places referencing
SearchBar.animationDuration (used in the animation modifiers around the view
state changes) to pass the value directly without casting; ensure the constant's
declaration (animationDuration) and all usages in animation(...) calls use the
TimeInterval/Double type.

Comment on lines +66 to +83
@ViewBuilder
private var clearButton: some View {
if !text.isEmpty {
Button(action: { text = "" }) {
Image(systemName: "xmark.circle.fill")
.foregroundColor(Color.black1000Alpha30)
}
}
}

private var cancelButton: some View {
Button(action: {
text = ""
withAnimation(.easeInOut(duration: Layout.animationDuration)) {
isEditing = false
}
isFocused = false
}) {
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 | 🟡 Minor

Fix the SwiftLint warnings in this component.

SwiftLint flags the @ViewBuilder placement and the Button(action:) { ... } trailing-label closures here. As per coding guidelines, **/*.swift: Use SwiftFormat and SwiftLint for Swift code formatting and linting as configured in .swiftformat and .swiftlint.yml files.

🧹 Proposed lint cleanup
-    `@ViewBuilder`
-    private var clearButton: some View {
+    `@ViewBuilder` private var clearButton: some View {
         if !text.isEmpty {
-            Button(action: { text = "" }) {
+            Button(action: { text = "" }, label: {
                 Image(systemName: "xmark.circle.fill")
                     .foregroundColor(Color.black1000Alpha30)
-            }
+            })
         }
     }
 
     private var cancelButton: some View {
         Button(action: {
             text = ""
             withAnimation(.easeInOut(duration: Layout.animationDuration)) {
                 isEditing = false
             }
             isFocused = false
-        }) {
+        }, label: {
             Text(NSLocalizedString("Cancel", comment: ""))
                 .padding(.horizontal, Layout.cancelHorizontalPadding)
                 .padding(.vertical, Layout.cancelVerticalPadding)
-        }
+        })
         .tint(.primaryText)
     }
-    `@ViewBuilder`
-    private var searchField: some View {
+    `@ViewBuilder` private var searchField: some View {

Also applies to: 108-110

🧰 Tools
🪛 SwiftLint (0.63.2)

[Warning] 67-67: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)


[Warning] 69-69: Trailing closure syntax should not be used when passing more than one closure argument

(multiple_closures_with_trailing_closure)


[Warning] 83-83: Trailing closure syntax should not be used when passing more than one closure argument

(multiple_closures_with_trailing_closure)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DashWallet/Sources/UI/SwiftUI` Components/SearchBar.swift around lines 66 -
83, Remove the unnecessary `@ViewBuilder` annotation from the clearButton computed
property and convert explicit Button(action: { ... }) { ... } forms to the
preferred trailing-label style using the closure-first syntax, e.g. change
Button(action: { text = "" }) { Image(...) } to Button { text = "" } label: {
Image(systemName: "xmark.circle.fill").foregroundColor(...) }, and do the same
refactor for cancelButton (change Button(action: { text = ""; withAnimation {
isEditing = false }; isFocused = false }) { ... } to Button { text = "";
withAnimation(.easeInOut(duration: Layout.animationDuration)) { isEditing =
false }; isFocused = false } label: { ... }), ensuring you update the label
closures accordingly.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
.github/workflows/release-xcode26.yml (1)

5-8: Redundant branch patterns.

release/** already matches everything release/* matches (and more), so listing both is redundant. Keep only release/** (or only release/* if you intentionally want to exclude nested paths like release/2026/04).

♻️ Proposed fix
   push:
     branches:
-      - release/*
       - release/**

Also consider adding a concurrency block so rapid pushes to the same release branch don't queue multiple 90-minute archive jobs:

concurrency:
  group: release-xcode26-${{ github.ref }}
  cancel-in-progress: true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release-xcode26.yml around lines 5 - 8, The push branch
patterns are redundant: remove the duplicate "release/*" entry and keep only
"release/**" in the push.branches list so nested release paths are still
matched; additionally add a concurrency block with group: release-xcode26-${{
github.ref }} and cancel-in-progress: true to prevent long-running duplicate
jobs from queuing (place the concurrency stanza at the top level of the workflow
alongside the push trigger).
fastlane/Fastfile (1)

85-92: Use fastlane's ensure_xcode_version action instead of rolling custom regex.

Replace the regex check with ensure_xcode_version(version: "26", strict: false) to match Xcode 26.x. The built-in action provides clearer error messages and avoids custom parsing logic. Note: this action depends on the deprecated xcode-install gem; for new projects, consider the xcodes action as an alternative.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fastlane/Fastfile` around lines 85 - 92, The custom Xcode detection in the
private_lane :ensure_xcode_26 currently parses xcodebuild output with a regex;
replace that logic by calling Fastlane's ensure_xcode_version action to validate
Xcode 26.x (e.g., ensure_xcode_version(version: "26", strict: false)) inside the
ensure_xcode_26 lane, removing the sh("xcodebuild -version") parsing and the
UI.user_error! path; if you need a non-deprecated alternative, consider using
the xcodes action instead and document that choice in the lane.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release-xcode26.yml:
- Around line 20-23: The workflow uses the maxim-lobanov/setup-xcode action with
an unsupported semver shorthand value for the input key xcode-version; change
the xcode-version value from "26.x" to a valid SemVer string or range (e.g.,
"26" to allow any 26.x or a specific patch like "26.3"), so update the
xcode-version field in the Select Xcode 26.x step to a supported format.
- Around line 35-38: The GitHub Actions step named "Setup CocoaPods" currently
uses maxim-lobanov/setup-cocoapods@v1 which is unmaintained; replace the action
reference with the actively maintained fork step-security/setup-cocoapods@v1
(preserve the existing `version: "1.15.2"` input and any other with/inputs) so
the step reads the same but points to the maintained action; update any workflow
documentation/comments that mention the old action name to reflect the new
action.

In `@fastlane/Fastfile`:
- Around line 23-24: The beta lane currently calls ensure_xcode_26 which raises
UI.user_error! and blocks developers on Xcode 15/16 from running fastlane beta
before cocoapods_install; remove or relax that hard guard in the beta lane by
either moving the ensure_xcode_26 call back to the CI-only lane
(release_archive_ci) or replacing the hard error in the ensure_xcode_26
invocation within the beta lane with a soft warning/log (e.g., warn and
continue) so local TestFlight pushes still work until the team migrates to Xcode
26; update the Fastfile references to ensure_xcode_26, beta, release_archive_ci,
cocoapods_install, and any UI.user_error! calls accordingly.
- Around line 49-65: The lane release_archive_ci currently duplicates CocoaPods
installation and is missing a CI-friendly build flag; remove the external pod
install --repo-update step from the workflow (release-xcode26.yml) so
cocoapods_install in the lane is the sole install, and update the build_app call
in release_archive_ci to include skip_profile_detection: true alongside
skip_codesigning: true to prevent provisioning/profile lookups on CI.

---

Nitpick comments:
In @.github/workflows/release-xcode26.yml:
- Around line 5-8: The push branch patterns are redundant: remove the duplicate
"release/*" entry and keep only "release/**" in the push.branches list so nested
release paths are still matched; additionally add a concurrency block with
group: release-xcode26-${{ github.ref }} and cancel-in-progress: true to prevent
long-running duplicate jobs from queuing (place the concurrency stanza at the
top level of the workflow alongside the push trigger).

In `@fastlane/Fastfile`:
- Around line 85-92: The custom Xcode detection in the private_lane
:ensure_xcode_26 currently parses xcodebuild output with a regex; replace that
logic by calling Fastlane's ensure_xcode_version action to validate Xcode 26.x
(e.g., ensure_xcode_version(version: "26", strict: false)) inside the
ensure_xcode_26 lane, removing the sh("xcodebuild -version") parsing and the
UI.user_error! path; if you need a non-deprecated alternative, consider using
the xcodes action instead and document that choice in the lane.
🪄 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: 5853d409-78cf-492d-a8bc-5e02a8a1f398

📥 Commits

Reviewing files that changed from the base of the PR and between c57de5e and a74a184.

📒 Files selected for processing (2)
  • .github/workflows/release-xcode26.yml
  • fastlane/Fastfile

Comment on lines +20 to +23
- name: Select Xcode 26.x
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "26.x"
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

🧩 Analysis chain

🌐 Web query:

maxim-lobanov/setup-xcode action xcode-version input accepted formats

💡 Result:

The xcode-version input for the maxim-lobanov/setup-xcode GitHub Action accepts the following formats: - latest (latest Xcode version including betas available on the runner) - latest-stable (latest stable Xcode version) - SemVer strings, such as: - 16 - 16.4 - 26.3 - ^16.2.0 (SemVer range) - SemVer string followed by -beta (e.g., 16-beta) to select among beta releases Specific versions should be wrapped in single quotes in YAML (e.g., '16.4') to preserve trailing zeros, as GitHub Actions trims them from numbers. Available versions depend on the macOS runner image (e.g., macos-latest, macos-15, macos-26); check the GitHub runner-images repository for the full list pre-installed on each image. Examples: - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: latest-stable - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '16.4'

Citations:


Replace xcode-version: "26.x" with a valid format — the "X.x" shorthand is not supported.

The setup-xcode action accepts SemVer strings (e.g., 26, 26.3) or SemVer ranges (e.g., ^26.0.0), but not the 26.x format. This will likely fail or silently fall back to the runner's default Xcode. Use xcode-version: "26" for any 26.x version, or specify an exact version like "26.3".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release-xcode26.yml around lines 20 - 23, The workflow
uses the maxim-lobanov/setup-xcode action with an unsupported semver shorthand
value for the input key xcode-version; change the xcode-version value from
"26.x" to a valid SemVer string or range (e.g., "26" to allow any 26.x or a
specific patch like "26.3"), so update the xcode-version field in the Select
Xcode 26.x step to a supported format.

Comment on lines +35 to +38
- name: Setup CocoaPods
uses: maxim-lobanov/setup-cocoapods@v1
with:
version: "1.15.2"
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 | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

github action maxim-lobanov/setup-cocoapods

💡 Result:

The GitHub Action maxim-lobanov/setup-cocoapods sets up a specific version of CocoaPods in GitHub Actions workflows, primarily for macOS and Ubuntu runners. It supports specifying the version directly via the 'version' input (e.g., 'latest', '1.9.0') or dynamically parsing it from a Podfile.lock file using the 'podfile-path' input. Only one of these inputs should be used at a time. Key details: - Repository: https://github.com/maxim-lobanov/setup-cocoapods (31 stars, MIT license) - Latest release: v1.4.0 (November 27, 2023) - Last commit: November 27, 2023 (no updates since, as of 2026-04-20) - Available on GitHub Marketplace: https://github.com/marketplace/actions/setup-cocoapods Example usage: - name: Setup CocoaPods uses: maxim-lobanov/setup-cocoapods@v1 with: version: 1.9.0 or - name: Setup CocoaPods from Podfile.lock uses: maxim-lobanov/setup-cocoapods@v1 with: podfile-path: Example/Podfile.lock Status and considerations: - Open issues: 7 (e.g., caching requests, permission errors on Linux) - Security: StepSecurity rates it 2/10 due to 17 detected vulnerabilities in dependencies, no branch protection, no security policy. No published GitHub security advisories. - Maintenance: Inactive since late 2023 (0 commits in last 90 days per sources). - Alternative: step-security/setup-cocoapods (forked March 2026, actively maintained as a secure drop-in replacement, last push April 2026). Note: Not certified by GitHub; third-party action.

Citations:


🏁 Script executed:

#!/bin/bash
# Confirm the action is referenced consistently and check if resolvable via the GitHub API.
gh api repos/maxim-lobanov/setup-cocoapods 2>&1 | head -20

Repository: dashpay/dashwallet-ios

Length of output: 5922


Consider using the actively maintained step-security/setup-cocoapods action instead.

The maxim-lobanov/setup-cocoapods action exists and is available on the GitHub Marketplace, so the workflow will resolve correctly. However, the original repository has been inactive since November 2023 and contains 17 detected vulnerabilities in its dependencies (security rating 2/10). A maintained fork, step-security/setup-cocoapods, was published in March 2026 and is actively updated as of April 2026, providing a secure drop-in replacement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release-xcode26.yml around lines 35 - 38, The GitHub
Actions step named "Setup CocoaPods" currently uses
maxim-lobanov/setup-cocoapods@v1 which is unmaintained; replace the action
reference with the actively maintained fork step-security/setup-cocoapods@v1
(preserve the existing `version: "1.15.2"` input and any other with/inputs) so
the step reads the same but points to the maintained action; update any workflow
documentation/comments that mention the old action name to reflect the new
action.

Comment thread fastlane/Fastfile
Comment on lines 23 to +24
lane :beta do
ensure_xcode_26
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 | 🟡 Minor

ensure_xcode_26 in beta blocks local TestFlight pushes on older Xcode.

Adding ensure_xcode_26 to the beta lane means any developer still on Xcode 15/16 can no longer run fastlane beta locally — they'll hit UI.user_error! before cocoapods_install. If TestFlight uploads are still done from developer machines (not only CI), consider either keeping the guard only on release_archive_ci, or making it a soft warning for beta until the whole team is on Xcode 26.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fastlane/Fastfile` around lines 23 - 24, The beta lane currently calls
ensure_xcode_26 which raises UI.user_error! and blocks developers on Xcode 15/16
from running fastlane beta before cocoapods_install; remove or relax that hard
guard in the beta lane by either moving the ensure_xcode_26 call back to the
CI-only lane (release_archive_ci) or replacing the hard error in the
ensure_xcode_26 invocation within the beta lane with a soft warning/log (e.g.,
warn and continue) so local TestFlight pushes still work until the team migrates
to Xcode 26; update the Fastfile references to ensure_xcode_26, beta,
release_archive_ci, cocoapods_install, and any UI.user_error! calls accordingly.

Comment thread fastlane/Fastfile
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.

2 participants