Skip to content

Replace Lottie connect button with toggle switch#140

Merged
pappz merged 8 commits into
netbirdio:mainfrom
evgeniyChepelev:feature/ios-home-toggle-switch
Jun 28, 2026
Merged

Replace Lottie connect button with toggle switch#140
pappz merged 8 commits into
netbirdio:mainfrom
evgeniyChepelev:feature/ios-home-toggle-switch

Conversation

@evgeniyChepelev

@evgeniyChepelev evgeniyChepelev commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Description

Summary

  • Replace the animated Lottie connect/disconnect button with a custom toggle switch (VPNToggleView), matching the desktop app's visual direction
  • Add optimistic UI: toggle thumb and pulse animation respond instantly on tap instead of waiting for OS confirmation
  • Simplify the connection screen to a monotone background (BgMenu), centered layout with SECURED / NOT SECURED status label and hostname/IP grouped under the toggle
  • Rebuild NetBirdSDK.xcframework from the current netbird-core submodule commit

Details

  • New component: VPNToggleView — orange/gray pill toggle with spring thumb animation and pulsing track during connecting/disconnecting
  • iOSConnectionView rewritten: removed Lottie dependency and two-tone background image overlay, all connect/disconnect logic (buttonLock, connect(), close()) preserved unchanged
  • FQDN/IP text moved under the toggle block, reduced to secondary color/smaller size; tap-to-copy behavior kept as-is (text swaps to "Copied" briefly)

Test plan

  • Verify toggle visually reflects: connected, disconnected, connecting, disconnecting
  • Confirm tap response is instant (no perceived delay) for connect and disconnect
  • Confirm tapping FQDN/IP copies to clipboard and shows "Copied" feedback
  • Verify no double-tap / race issues while buttonLock is active
  • Check accessibility tap area on the toggle

Summary by CodeRabbit

  • New Features
    • Added an animated VPN on/off toggle with optimistic UI updates during state transitions.
    • Expanded the connection details panel to include IPv4 and IPv6, with tap-to-copy and “Copied” feedback.
    • Connection info now includes IPv6 alongside FQDN and IPv4, refreshed during polling and retained for the active profile.
  • Style
    • Refreshed the iOS connection screen layout to a more centered presentation, keeping existing warning banner behavior.
    • Bundled Inter and JetBrains Mono variable fonts for improved typography.

- Add VPNToggleView: custom pill toggle with spring animation and pulse
  during connecting/disconnecting states
- Optimistic UI: thumb moves instantly on tap, pulse starts before OS confirms
- Simplify iOSConnectionView: monotone BgMenu background, centered layout,
  SECURED/NOT SECURED status label, hostname/IP grouped under toggle
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e55e8f3b-4153-444d-954e-605dc09233a7

📥 Commits

Reviewing files that changed from the base of the PR and between 64c7e33 and 11e8e97.

📒 Files selected for processing (2)
  • NetBird.xcodeproj/project.pbxproj
  • NetBird/Source/App/ViewModels/MainViewModel.swift
 _________________________________________
< Time zones: the final boss of software. >
 -----------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
📝 Walkthrough

Walkthrough

A new VPNToggleView SwiftUI component (iOS-only) is added with optimistic state, looping pulse animation during transitions, and tap-lock support. IPv6 address tracking is introduced end-to-end through ProfileConnectionCache, MainViewModel, and iOSConnectionView. The connection UI is refactored to replace Lottie animation and GeometryReader layout with a centered VStack using VPNToggleView, and IP addresses are now displayed with an expandable dropdown that shows both IPv4 and IPv6. New font resources are registered in the Xcode project.

Changes

Connection UI Modernization with IPv6 Support

Layer / File(s) Summary
IPv6 data persistence in ProfileConnectionCache
NetbirdKit/ProfileConnectionCache.swift
ProfileConnectionEntry adds optional ipv6 field. save(ip:fqdn:ipv6:for:) method signature extended to accept ipv6 parameter. Cache initialization updated to include ipv6 (defaulting to nil). clearConnectionData(for:) now also clears cached ipv6.
IPv6 tracking in MainViewModel polling and profile loading
NetBird/Source/App/ViewModels/MainViewModel.swift
Introduces @Published var ipv6 property. Loads cached ipv6 during profile selection and init. Polling loop now detects details.ipv6 changes, updates published ipv6, and persists it to cache alongside IP and FQDN. clearDetails() also clears ipv6.
VPNToggleView: optimistic state, pulse animation, tap handling
NetBird/Source/App/Views/Components/VPNToggleView.swift
New SwiftUI struct (iOS-only) with optimisticIsOn override state responding instantly to tap, isTransitioning computed property, async looping opacity pulse keyed to transitioning state, tap gesture guarded by isLocked, and onChange observer clearing optimistic override when vpnState updates.
iOSConnectionView: layout, IPv6 display, copy feedback, navigation
NetBird/Source/App/Views/iOS/iOSConnectionView.swift
Replaces GeometryReader/background/Lottie layout with centered ZStack/VStack. Adds state for per-item copy feedback (fqdnCopied, ipv4Copied, ipv6Copied, showIPDetails). Refactors body to use VPNToggleView wired to connect()/close(), expandable IP dropdown showing IPv4 and IPv6 rows, and shared copy helper with haptic feedback. Preserves offline warning banner, hidden navigation links, updated placeholder, and Safari login modal.
Font resources and build phases
NetBird/Info.plist, NetBird.xcodeproj/project.pbxproj
Registers inter-variable.ttf and jetbrains-mono-variable.ttf in UIAppFonts plist key. Adds font build files, references, new Fonts group, and resource phases across NetBird and extension targets. Wires VPNToggleView.swift into build via PBXBuildFile, PBXFileReference, Components group, and source build phase.

Sequence Diagram(s)

sequenceDiagram
  participant User as User tap
  participant Toggle as VPNToggleView
  participant ViewModel as MainViewModel
  participant Cache as ProfileConnectionCache
  participant Poll as Polling details
  User->>Toggle: tap toggle
  Toggle->>Toggle: set optimisticIsOn immediately
  Toggle->>ViewModel: call onConnect() / onDisconnect()
  ViewModel->>Poll: await details.vpnState change
  Poll-->>ViewModel: receive updated vpnState + ipv6
  ViewModel->>Cache: persist ipv6 to cache
  ViewModel->>Toggle: vpnState onChange clears optimisticIsOn
  Toggle-->>User: toggle settled to final state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly Related PRs

  • netbirdio/ios-client#55: Main PR switches the iOS connection button UI from the prior CustomLottieView setup to the new VPNToggleView, while the retrieved PR specifically fixes CustomLottieView/MainViewModel behavior using networkUnavailable—both are directly about VPN connect/disconnect button state when offline.
  • netbirdio/ios-client#60: Both PRs modify the same iOS VPN connection UI layer—iOSConnectionView.swift (and related state in MainViewModel.swift)—so the main PR's VPNToggleView/IPv6 changes are directly adjacent to (and potentially overlapping with) the retrieved PR's earlier connection-state/UI redesign.

Suggested Reviewers

  • pappz

Poem

🐰 A toggle springs to life with grace,
IPv6 flows through every place,
The pulse fades softly, fonts align,
No Lottie frames—just SwiftUI shine!
With copy feedback and dropdown dreams,
This refactor's lovelier than it seems. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Replace Lottie connect button with toggle switch' clearly and concisely summarizes the main change: replacing an animated Lottie button with a custom toggle switch component.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering summary, detailed changes, test plan, and all major modifications including the new VPNToggleView component and refactored iOSConnectionView.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@NetBird/Source/App/Views/iOS/iOSConnectionView.swift`:
- Around line 104-106: The `.smooth` animation curve is not compatible with the
iOS 15 deployment target, as it was only introduced in iOS 17. In
NetBird/Source/App/Views/iOS/iOSConnectionView.swift at lines 104-106, replace
the `.smooth` animation in the withAnimation call with a compatible animation
curve such as `.easeInOut` or `.default`. Apply the same change to all other
instances of `.smooth` animation in the file, including the ones at lines
121-123, to ensure compatibility across all iOS 15+ devices.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 264cf8ae-3b6b-4fd5-9420-fc8d1568567f

📥 Commits

Reviewing files that changed from the base of the PR and between 372647a and 2ebc895.

📒 Files selected for processing (3)
  • NetBird.xcodeproj/project.pbxproj
  • NetBird/Source/App/Views/Components/VPNToggleView.swift
  • NetBird/Source/App/Views/iOS/iOSConnectionView.swift

Comment thread NetBird/Source/App/Views/iOS/iOSConnectionView.swift Outdated
@evgeniyChepelev

Copy link
Copy Markdown
Collaborator Author

/testflight

@github-actions

Copy link
Copy Markdown

TestFlight builds uploaded 0.2.1 (45) for e168af2 — iOS + tvOS

View workflow run

@pappz

pappz commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

/testflight

@github-actions

Copy link
Copy Markdown

TestFlight builds uploaded 0.2.1 (54) for e168af2 — iOS + tvOS

View workflow run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
NetBird/Source/App/Views/iOS/iOSConnectionView.swift (1)

82-86: 💤 Low value

Chevron rotation appears inverted from standard disclosure pattern.

When showAddressDetails is false, the chevron points up (180° rotation); when true, it points down (0°). Standard UX typically shows a down-pointing chevron for "tap to expand" and up-pointing for "tap to collapse."

Suggested fix to follow standard disclosure pattern
                                     Image(systemName: "chevron.down")
                                         .font(.system(size: 12, weight: .semibold))
                                         .foregroundColor(Color("TextSecondary"))
-                                        .rotationEffect(.degrees(showAddressDetails ? 0 : 180))
+                                        .rotationEffect(.degrees(showAddressDetails ? 180 : 0))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@NetBird/Source/App/Views/iOS/iOSConnectionView.swift` around lines 82 - 86,
The chevron rotation logic in the rotationEffect modifier is inverted from the
standard disclosure pattern. In the iOSConnectionView file, the Image with
systemName "chevron.down" currently rotates 0 degrees when showAddressDetails is
true and 180 degrees when false, which is opposite to standard UX expectations.
Swap the rotation values in the rotationEffect call so that when
showAddressDetails is true (expanded state), the chevron rotates 180 degrees,
and when showAddressDetails is false (collapsed state), it rotates 0 degrees.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@NetBird/Source/App/Views/iOS/iOSConnectionView.swift`:
- Line 53: The font family names in the .font(.custom()) calls do not exactly
match the registered variable font names, causing silent fallback to the system
font. Update the font family parameter in the .custom() method at line 53 from
"InterVariable" to "Inter Variable" (adding a space), and update the font family
parameter in the second .custom() call at line 59 from "JetBrainsMono-Regular"
to "JetBrains Mono". SwiftUI requires exact matches for font family names,
including spaces and case sensitivity.

---

Nitpick comments:
In `@NetBird/Source/App/Views/iOS/iOSConnectionView.swift`:
- Around line 82-86: The chevron rotation logic in the rotationEffect modifier
is inverted from the standard disclosure pattern. In the iOSConnectionView file,
the Image with systemName "chevron.down" currently rotates 0 degrees when
showAddressDetails is true and 180 degrees when false, which is opposite to
standard UX expectations. Swap the rotation values in the rotationEffect call so
that when showAddressDetails is true (expanded state), the chevron rotates 180
degrees, and when showAddressDetails is false (collapsed state), it rotates 0
degrees.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c82acf9-a11e-4345-906d-c88262db6a52

📥 Commits

Reviewing files that changed from the base of the PR and between 2ebc895 and d779d30.

⛔ Files ignored due to path filters (2)
  • NetBird/Fonts/inter-variable.ttf is excluded by !**/*.ttf
  • NetBird/Fonts/jetbrains-mono-variable.ttf is excluded by !**/*.ttf
📒 Files selected for processing (5)
  • NetBird.xcodeproj/project.pbxproj
  • NetBird/Info.plist
  • NetBird/Source/App/ViewModels/MainViewModel.swift
  • NetBird/Source/App/Views/iOS/iOSConnectionView.swift
  • NetbirdKit/ProfileConnectionCache.swift
✅ Files skipped from review due to trivial changes (1)
  • NetBird/Info.plist

Comment thread NetBird/Source/App/Views/iOS/iOSConnectionView.swift Outdated
@evgeniyChepelev

Copy link
Copy Markdown
Collaborator Author

/testflight

@github-actions

Copy link
Copy Markdown

TestFlight builds uploaded 0.2.1 (55) for d779d30 — iOS + tvOS

View workflow run

@pappz

pappz commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

/testflight

@github-actions

Copy link
Copy Markdown

TestFlight builds uploaded 0.2.1 (56) for d779d30 — iOS + tvOS

View workflow run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
NetBird/Source/App/ViewModels/MainViewModel.swift (1)

498-499: 💤 Low value

Consider including IPv6 in widget state for completeness.

The widget state update includes ip and fqdn but not ipv6. If the widget has space to display IPv6 information (similar to the main connection view), consider adding:

userDefaults?.set(ipv6, forKey: GlobalConstants.keyWidgetIPv6)

If the widget intentionally excludes IPv6 due to space constraints, this can be ignored.

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

In `@NetBird/Source/App/ViewModels/MainViewModel.swift` around lines 498 - 499,
The widget state update is missing IPv6 information. After the lines where ip
and fqdn are stored to userDefaults using userDefaults?.set with
GlobalConstants.keyWidgetIP and GlobalConstants.keyWidgetFQDN respectively, add
a corresponding line to store ipv6 to userDefaults using
GlobalConstants.keyWidgetIPv6 as the key to ensure the widget has access to
complete network information (unless IPv6 is intentionally excluded due to space
constraints in the widget display).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@NetBird/Source/App/ViewModels/MainViewModel.swift`:
- Around line 498-499: The widget state update is missing IPv6 information.
After the lines where ip and fqdn are stored to userDefaults using
userDefaults?.set with GlobalConstants.keyWidgetIP and
GlobalConstants.keyWidgetFQDN respectively, add a corresponding line to store
ipv6 to userDefaults using GlobalConstants.keyWidgetIPv6 as the key to ensure
the widget has access to complete network information (unless IPv6 is
intentionally excluded due to space constraints in the widget display).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67a9371f-ab9c-43d3-bc5b-630729d50439

📥 Commits

Reviewing files that changed from the base of the PR and between d779d30 and c642f45.

📒 Files selected for processing (4)
  • NetBird.xcodeproj/project.pbxproj
  • NetBird/Source/App/ViewModels/MainViewModel.swift
  • NetBird/Source/App/Views/Components/VPNToggleView.swift
  • NetBird/Source/App/Views/iOS/iOSConnectionView.swift
🚧 Files skipped from review as they are similar to previous changes (3)
  • NetBird/Source/App/Views/Components/VPNToggleView.swift
  • NetBird.xcodeproj/project.pbxproj
  • NetBird/Source/App/Views/iOS/iOSConnectionView.swift

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@pappz

pappz commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

/testflight

@github-actions

Copy link
Copy Markdown

Build failed (iOS, tvOS) 0.2.1 (58) for 11e8e97

View workflow run

@pappz

pappz commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

/testflight

@github-actions

Copy link
Copy Markdown

TestFlight builds uploaded 0.2.1 (65) for 11e8e97 — iOS + tvOS

View workflow run

@pappz pappz merged commit 3ccb66a into netbirdio:main Jun 28, 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.

2 participants