Skip to content

fix: support iOS 26#764

Merged
HashEngineering merged 18 commits into
masterfrom
fix/support-iOS-26
Apr 25, 2026
Merged

fix: support iOS 26#764
HashEngineering merged 18 commits into
masterfrom
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

Release Notes

  • New Features

    • Added a new local currency selector screen with integrated search functionality for easier currency browsing.
    • Redesigned payment access through the tab bar for streamlined navigation.
  • Bug Fixes

    • Fixed biometrics confirmation text display when Touch ID or Face ID is unavailable on device.
    • Corrected back button layout alignment in navigation.
  • UI Improvements

    • Modernized currency selection interface with enhanced visual consistency.
    • Updated color scheme and spacing for improved visual hierarchy.

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

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new SwiftUI-based local currency/fiat selector interface system with supporting geometry tracking components. It refactors the payment tab from a custom button to native tab bar integration, updates spacing/styling across views, adds color assets, and enhances build configuration for Xcode 26 compatibility and preview support.

Changes

Cohort / File(s) Summary
Local Currency Selection System
LocalCurrencyView.swift, LocalCurrencyViewModel.swift, LocalCurrencyCellView.swift, CurrencyItem.swift
New SwiftUI currency selector with searchable list, flag rendering, and selection callbacks. View model manages currency items, search filtering (debounced 150ms), and fiat currency persistence. Cell view displays currency code/name with optional price and selection indicator.
SwiftUI Layout & Scroll Components
FrameReader.swift, LocationReader.swift, ScrollViewWithOnScrollChanged.swift
New public geometry tracking components: FrameReader emits view frame changes via closure; LocationReader reports center coordinates; ScrollViewWithOnScrollChanged attaches scroll offset tracking with named coordinate spaces.
Search & Navigation Components
SearchBar.swift, Color+DWStyle.swift
New SearchBar with text binding, focus-aware cancel button, and clear action. Added searchBg color and black alpha variants (black1000Alpha30, black1000Alpha50) to color extensions.
Payment Tab Bar Refactoring
MainTabbarController.swift
Replaced custom paymentButton UI element with tab-bar-based integration using boolean paymentIsOpened flag. Payment tab now uses generated blue circle icon; tab selection intercepts modal presentation instead of switching tabs.
Home Screen Pay Delegation
HomeViewController+Shortcuts.swift
Added conditional DWContactsViewControllerPayDelegate conformance when DASHPAY enabled; now creates DWContactsViewController with payModel/dataProvider and .payToSelector intent, triggering performPay(toUser:) on contact selection.
Views & Controller Updates
ShortcutsView.swift, BaseNavigationController.swift, SettingsScreen.swift
Adjusted ShortcutsView vertical padding (8.0 constant applied to height/insets). Removed custom back button image edge insets. Migrated SettingsScreen currency selector from UIKit delegate to SwiftUI LocalCurrencyView in UIHostingController.
Asset & Color Definitions
Shared/Resources/SharedAssets.xcassets/Colors/*
Added SearchBackground color (rgba with 10% alpha); created Black1000Alpha30 and Black1000Alpha50 variants. Reformatted existing color JSON (NumberKeyboardHighlightedTextColor, ShadowColor, TertiaryBackgroundColor) to use hex notation.
Project Configuration
DashWallet.xcodeproj/project.pbxproj
Bumped objectVersion to 70; added four plist file references (Coinbase, ZenLedger, GoogleService, Topper); registered new Swift source files (LocalCurrency*, SearchBar, geometry readers); introduced PBXFileSystemSynchronizedRootGroup for Geometry Readers and ScrollViews.
Build & Scheme Configuration
DashWallet.xcodeproj/xcshareddata/xcschemes/dashwallet.xcscheme, fastlane/Fastfile, AppDelegate.m, BuySellPortal.storyboard
Changed scheme launch build config from Testflight to Debug. Added ensure_xcode_26 lane and new release_archive_ci lane; beta lane now sets workspace and buildlog path. AppDelegate returns early in DEBUG when running for Xcode previews. Removed disabled bar button from Buy Sell Portal storyboard.
Minor Security & Logic Fixes
DWBaseAdvancedSecurityModel.m
Updated spending confirmation description to fall back to localized "biometrics" string when neither Touch ID nor Face ID available, replacing prior assertion-failure behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐰 A hop through currencies and SwiftUI streams,
Geometry readers fulfill all our dreams!
The search bar now glows with a soft amber light,
While payment tabs dance in their new tab-bar flight.
Configuration complete, our rabbit declares with delight! 🎨✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
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.
Title check ❓ Inconclusive The title 'fix: support iOS 26' is vague and does not clearly reflect the breadth of changes. The PR contains 13 modified files across UI components, view models, color assets, build configuration, and fastlane scripts—changes that go beyond a simple 'fix' for iOS 26 support. Consider a more descriptive title that captures the main changes, such as 'refactor: iOS 26 support with currency selector, payment tab improvements, and build updates' or specify the primary focus if one area dominates the changeset.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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

Comment thread DashWallet/Sources/UI/Home/HomeViewController+Shortcuts.swift
Comment thread DashWallet/Sources/UI/Main/MainTabbarController.swift Outdated
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

Comment thread DashWallet.xcodeproj/project.pbxproj
Comment thread DashWallet.xcodeproj/project.pbxproj
Comment thread DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift
Comment thread DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift
Comment thread DashWallet/Sources/UI/SwiftUI Components/SearchBar.swift
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 thread .github/workflows/release-xcode26.yml Outdated
Comment thread .github/workflows/release-xcode26.yml Outdated
Comment thread fastlane/Fastfile
Comment thread fastlane/Fastfile
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)

40-46: .payment enum cases are now dead code.

Since the Payment tab item is built via makePaymentTabImage() in configureControllers(), the .payment branches in both icon and selectedIcon are no longer referenced. You can drop the case (or route it through makePaymentTabImage()) to avoid stale code.

♻️ Proposed cleanup
     var icon: UIImage {
         let name: String
 
         switch self {
         case .home:
             name = "tabbar_home_icon"
         case .contacts:
             name = "tabbar_contacts_icon"
-        case .payment:
-            name = "tabbar_pay_button"
         case .explore:
             name = "tabbar_discover_icon"
         case .more:
             name = "tabbar_other_icon"
+        case .payment:
+            // Rendered via MainTabbarController.makePaymentTabImage()
+            return UIImage()
         }
 
         return UIImage(named: name)!.withRenderingMode(.alwaysOriginal)
     }

(Apply the analogous change to selectedIcon.)

Also applies to: 59-65

🤖 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 40 - 46,
The .payment enum branches in the MainTabbarItem's icon and selectedIcon are
dead now that the Payment tab image is provided by makePaymentTabImage() in
configureControllers(); remove the .payment case entries (or have them call
through to makePaymentTabImage()) in both icon and selectedIcon so the enum
switch isn't stale, updating MainTabbarItem.icon and MainTabbarItem.selectedIcon
to either omit .payment or return the same image produced by
makePaymentTabImage().
🤖 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/Main/MainTabbarController.swift`:
- Around line 192-198: The Payment tab's UITabBarItem (created as item and
assigned to paymentVC of type EmptyController) lacks an accessibilityLabel so
VoiceOver users get no meaningful announcement; set item.accessibilityLabel to a
clear, localized string (e.g., "Payments" via NSLocalizedString) before
assigning it to paymentVC.tabBarItem so the tab is announced properly (do not
rely on accessibilityIdentifier).
- Around line 245-264: The closure passed to UIGraphicsImageRenderer.image in
makePaymentTabImage() declares an unused parameter named context which triggers
SwiftLint; update the closure signature to use an unnamed parameter (_) instead
of context (i.e., change { context in ... } to { _ in ... }) so the unused
parameter warning is resolved while keeping the drawing logic in
makePaymentTabImage intact.

---

Nitpick comments:
In `@DashWallet/Sources/UI/Main/MainTabbarController.swift`:
- Around line 40-46: The .payment enum branches in the MainTabbarItem's icon and
selectedIcon are dead now that the Payment tab image is provided by
makePaymentTabImage() in configureControllers(); remove the .payment case
entries (or have them call through to makePaymentTabImage()) in both icon and
selectedIcon so the enum switch isn't stale, updating MainTabbarItem.icon and
MainTabbarItem.selectedIcon to either omit .payment or return the same image
produced by makePaymentTabImage().
🪄 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: 3da3a1fd-b199-4147-9ccc-6e85dbec18cf

📥 Commits

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

📒 Files selected for processing (6)
  • DashWallet.xcodeproj/project.pbxproj
  • DashWallet/Sources/UI/Home/HomeViewController+Shortcuts.swift
  • DashWallet/Sources/UI/Main/MainTabbarController.swift
  • DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift
  • DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift
  • DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
💤 Files with no reviewable changes (1)
  • DashWallet/Sources/UI/Views/Navigation/BaseNavigationController.swift
🚧 Files skipped from review as they are similar to previous changes (3)
  • DashWallet/Sources/UI/SwiftUI Components/Color+DWStyle.swift
  • DashWallet/Sources/UI/Menu/Settings/SettingsScreen.swift
  • DashWallet.xcodeproj/project.pbxproj

Comment thread DashWallet/Sources/UI/Main/MainTabbarController.swift
Comment thread DashWallet/Sources/UI/Main/MainTabbarController.swift
@romchornyi romchornyi self-requested a review April 25, 2026 19:00
@HashEngineering HashEngineering merged commit b44fd57 into master Apr 25, 2026
3 checks passed
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.

3 participants