Conversation
- Add NetBirdWidgetExtension target with small/medium widget sizes - Support direct connect/disconnect from widget via interactive buttons (iOS 17+) - Detect missing VPN config or login-required state and open app via deep link - Poll for stable VPN state after toggle to prevent loader getting stuck - Add widget shared state keys to GlobalConstants and sync status from MainViewModel - Fix false "authentication required" alert on app resume after widget disconnect - Deduplicate app activation logic into shared startActivation/stopActivation - Extract polling helpers: updateDetailsIfChanged, updatePeersIfChanged, applyExtensionStatus
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a WidgetKit extension (views, timeline provider, intents, helpers, assets, entitlements), wires it into the Xcode project, persists widget state via app-group UserDefaults, adds deep-link handling, and updates app lifecycle/ViewModel activation and build versioning. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Widget as Widget UI
participant Intent as AppIntent
participant Defaults as App-Group UserDefaults
participant Manager as NETunnelProviderManager
participant Timeline as WidgetCenter
User->>Widget: Tap action button
Widget->>Intent: Invoke Connect/Disconnect/Toggle Intent
Intent->>Defaults: Read persisted vpn/ip/fqdn/login flag
Intent->>Manager: VPNIntentHelpers.loadManager()
alt Manager exists
Intent->>Manager: startVPNTunnel()/stopVPNTunnel()
Intent->>Intent: waitForStableState(manager)
Intent->>Defaults: Write interim/final widget vpn status
Intent->>Timeline: reloadAllTimelines()
else No manager
Intent->>Timeline: reloadAllTimelines()
end
Timeline->>Widget: Widget updates with new entry
sequenceDiagram
participant OS as iOS
participant App as NetBird App
participant VM as MainViewModel
participant Defaults as App-Group UserDefaults
participant WidgetCenter as WidgetCenter
OS->>App: Open URL netbird://connect or netbird://disconnect
App->>VM: handleWidgetURL(...)
alt Should connect
VM->>VM: connect()
else Should disconnect
VM->>VM: close()
end
VM->>VM: updateVPNDisplayState()
VM->>Defaults: persist status, ip, fqdn
VM->>WidgetCenter: WidgetCenter.reloadAllTimelines()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (3)
NetBirdWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json (1)
2-6: Consider setting explicit AccentColor componentsLike the background colorset, this is currently a placeholder entry only. Defining actual color components will make accent usage deterministic across widget states/themes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@NetBirdWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json` around lines 2 - 6, The AccentColor colorset currently only contains a placeholder "colors" array with an "idiom": "universal" entry; update the AccentColor Contents.json to include explicit color component definitions (e.g., color entries with "color" objects specifying "components" for "red","green","blue","alpha" and optional "appearances" for light/dark) so the AccentColor is deterministic across widget states/themes—locate the AccentColor.colorset Contents.json and replace the placeholder object under "colors" with properly formed color dictionaries specifying the components for the accent (and add separate appearance entries if needed).NetBirdWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json (1)
2-6: Consider defining an explicitWidgetBackgroundcolor valueThis colorset currently has no concrete color components. If Line 2–6 is intentionally a placeholder, fine; otherwise add explicit light/dark values to avoid accidental default/empty rendering in widget backgrounds.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@NetBirdWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json` around lines 2 - 6, The WidgetBackground colorset currently has an empty colors array (only "idiom":"universal"); replace that placeholder with explicit color entries in Contents.json: add a "color" object with "color-space":"srgb" and concrete "components" (red/green/blue/alpha) for the default/light appearance and, if you need a dark-mode variant, add a separate entry with an "appearances":[{"appearance":"luminosity","value":"dark"}] and its own "color" components; target the existing WidgetBackground colorset and update the "colors" array (and optionally include a "name" if desired) so the widget background will render deterministically in light and dark modes.NetBirdWidgetExtension/WidgetConstants.swift (1)
4-11: Keep the shared suite/key strings in one place.
appGroupSuite,keyVPNStatus,keyIP,keyFQDN, andkeyLoginRequirednow duplicate values that already exist inNetbirdKit/GlobalConstants.swift. Since the app and widget communicate via raw strings, drift here will break state sharing silently. Prefer reusing the shared constants in the widget target, or add a small regression test that asserts the values stay aligned.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@NetBirdWidgetExtension/WidgetConstants.swift` around lines 4 - 11, WidgetConstants.swift duplicates shared string constants (appGroupSuite, keyVPNStatus, keyIP, keyFQDN, keyLoginRequired) that are defined in NetbirdKit.GlobalConstants; update the widget to reuse the single source of truth by importing/accessing NetbirdKit.GlobalConstants (or move the shared values into a new shared module) and remove the duplicated literals from WidgetConstants, and optionally add a small regression test that asserts WidgetConstants' values match NetbirdKit.GlobalConstants to prevent future drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@NetBird/Source/App/NetBirdApp.swift`:
- Around line 56-63: The .onOpenURL handler is currently attached only to
MainView inside the if let viewModel = viewModelLoader.viewModel block (and
similarly missing on loadingView), so cold-launch URLs can be missed; move the
.onOpenURL modifier to the scene root (e.g., on the WindowGroup/Scene where
MainView and loadingView are returned) so it is always installed, and have that
handler call handleWidgetURL(_:, viewModel:) when viewModel is available or
queue the URL for later processing if viewModelLoader.viewModel is nil; update
any references to MainView, loadingView, viewModelLoader.viewModel, and
handleWidgetURL accordingly.
- Around line 97-113: The startActivation Task can resume after suspension even
if cancelled; update startActivation to check Task.isCancelled after the await
to bail out early and avoid running side effects: after awaiting
networkExtensionAdapter.loadCurrentConnectionState() verify Task.isCancelled and
return if cancelled, and also check Task.isCancelled again before calling
viewModel.checkExtensionState(), viewModel.checkLoginRequiredFlag(), and
viewModel.startPollingDetails(); keep using activationTask?.cancel() as-is but
add these cooperative cancellation checks referencing startActivation,
activationTask, ViewModel.networkExtensionAdapter.loadCurrentConnectionState(),
viewModel.checkExtensionState(), viewModel.checkLoginRequiredFlag(), and
viewModel.startPollingDetails().
In `@NetBird/Source/App/ViewModels/MainViewModel.swift`:
- Around line 431-449: The method updateDetailsIfChanged currently ignores
updates when details.ip or details.fqdn are empty and doesn't propagate
IP/FQDN-only changes to app-group defaults or the widget; modify
updateDetailsIfChanged to treat an empty string as a valid change (compare
details.ip != ip and details.fqdn != fqdn without requiring non-empty), always
write the new values to UserDefaults (defaults.set(...)) and assign ip/fqdn when
they differ, and ensure updateVPNDisplayState() is called whenever any of ip,
fqdn, or managementStatus changes (not only on managementStatus) so widget sync
happens; keep the existing managementStatus handling (managementStatus !=
managementStatus) and preserve ordering of assignments.
- Line 18: The unconditional import WidgetKit in MainViewModel.swift causes tvOS
build failures; wrap the import in a platform check (e.g., `#if` os(iOS) or `#if`
canImport(WidgetKit)) so WidgetKit is only imported for supported platforms, and
ensure any WidgetKit-dependent code in MainViewModel is similarly guarded
(reference the import line and any WidgetKit-using symbols in MainViewModel).
- Around line 765-774: After clearing GlobalConstants.keyLoginRequired in the
shared UserDefaults (the block that calls userDefaults?.set(false, forKey:
GlobalConstants.keyLoginRequired) and userDefaults?.synchronize()), trigger a
widget timeline refresh so VPNStatusProvider (which reads needsAppSetup from
that key) updates immediately; add a call to reload widget timelines (e.g., via
WidgetCenter.shared.reloadAllTimelines()) right after synchronizing, keeping the
existing AppLogger.log, showAuthenticationRequired, and
scheduleLoginRequiredNotification() behavior intact.
In `@NetBirdWidgetExtension/NetBirdWidget.swift`:
- Around line 26-29: The .background() call on WidgetEntryView is invalid
(zero-argument) and causes a compile error; in NetBirdWidget.swift locate the
WidgetEntryView(entry: entry) chain and either remove the .background() call for
the iOS 15/16 branch or replace it with an explicit style (e.g., a Color or a
suitable background modifier) to match the iOS 17 branch; update the code around
WidgetEntryView(entry: entry).padding() so it no longer invokes background()
with no parameters.
In `@NetBirdWidgetExtension/NetBirdWidgetExtension.entitlements`:
- Around line 9-12: Remove the Network Extension entitlement from the WidgetKit
extension by deleting the com.apple.developer.networking.networkextension key
and its packet-tunnel-provider value from this entitlements file; instead place
the packet-tunnel-provider entitlement in a containing app target or a dedicated
Network Extension (packet tunnel provider) target, and ensure the WidgetKit
extension uses the com.apple.widget-extension extension point without network
extension entitlements to avoid signing/App Store validation failures.
In `@NetBirdWidgetExtension/Views/SmallWidgetView.swift`:
- Around line 16-23: SmallWidgetView currently only shows WidgetActionButton for
iOS 17+ leaving iOS 15/16 users without any action; add a fallback that mirrors
MediumWidgetView by providing a Link (or setting widgetURL) that triggers the
same connect/disconnect behavior for older OSes. Locate the WidgetActionButton
usage inside SmallWidgetView and replace the conditional branch so the iOS 17+
path keeps WidgetActionButton(entry: entry) { ProgressView()... } label: {
pillLabel(isConnected:) } and the else path renders a Link (or attaches
widgetURL) that wraps pillLabel(isConnected:) and points to the same URL/action
used by the WidgetActionButton so pre-iOS17 widgets remain interactive.
In `@NetBirdWidgetExtension/VPNStatusProvider.swift`:
- Around line 38-47: When loading the VPN status inside the
NETunnelProviderManager.loadAllFromPreferences closure, prefer the persisted
transition state written by ToggleVPNIntent (read from defaults using
WidgetConstants.keyVPNStatus) when it represents a transient state (.connecting
or .disconnecting); currently the code always uses manager.connection.status if
a manager exists which hides those persisted transitions. Change the logic in
the closure (the block that computes `status: WidgetVPNStatus`) to first read
the persisted raw status, convert to WidgetVPNStatus, and if that value is
.connecting or .disconnecting return it; otherwise fall back to using
`manager.connection.status`, and keep the existing fallback to reading the
persisted value or .disconnected when manager is nil.
---
Nitpick comments:
In `@NetBirdWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json`:
- Around line 2-6: The AccentColor colorset currently only contains a
placeholder "colors" array with an "idiom": "universal" entry; update the
AccentColor Contents.json to include explicit color component definitions (e.g.,
color entries with "color" objects specifying "components" for
"red","green","blue","alpha" and optional "appearances" for light/dark) so the
AccentColor is deterministic across widget states/themes—locate the
AccentColor.colorset Contents.json and replace the placeholder object under
"colors" with properly formed color dictionaries specifying the components for
the accent (and add separate appearance entries if needed).
In
`@NetBirdWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json`:
- Around line 2-6: The WidgetBackground colorset currently has an empty colors
array (only "idiom":"universal"); replace that placeholder with explicit color
entries in Contents.json: add a "color" object with "color-space":"srgb" and
concrete "components" (red/green/blue/alpha) for the default/light appearance
and, if you need a dark-mode variant, add a separate entry with an
"appearances":[{"appearance":"luminosity","value":"dark"}] and its own "color"
components; target the existing WidgetBackground colorset and update the
"colors" array (and optionally include a "name" if desired) so the widget
background will render deterministically in light and dark modes.
In `@NetBirdWidgetExtension/WidgetConstants.swift`:
- Around line 4-11: WidgetConstants.swift duplicates shared string constants
(appGroupSuite, keyVPNStatus, keyIP, keyFQDN, keyLoginRequired) that are defined
in NetbirdKit.GlobalConstants; update the widget to reuse the single source of
truth by importing/accessing NetbirdKit.GlobalConstants (or move the shared
values into a new shared module) and remove the duplicated literals from
WidgetConstants, and optionally add a small regression test that asserts
WidgetConstants' values match NetbirdKit.GlobalConstants to prevent future
drift.
🪄 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: 4dd22e8b-16f8-41b2-bea8-b24715379f53
⛔ Files ignored due to path filters (6)
NetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu 1.pngis excluded by!**/*.pngNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu.pngis excluded by!**/*.pngNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu@2x 1.pngis excluded by!**/*.pngNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu@2x.pngis excluded by!**/*.pngNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu@3x 1.pngis excluded by!**/*.pngNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/netbird-logo-menu@3x.pngis excluded by!**/*.png
📒 Files selected for processing (22)
NetBird.xcodeproj/project.pbxprojNetBird/Info.plistNetBird/Source/App/NetBirdApp.swiftNetBird/Source/App/ViewModels/MainViewModel.swiftNetBirdWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.jsonNetBirdWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.jsonNetBirdWidgetExtension/Assets.xcassets/Contents.jsonNetBirdWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.jsonNetBirdWidgetExtension/Assets.xcassets/netbird-logo-menu.imageset/Contents.jsonNetBirdWidgetExtension/Info.plistNetBirdWidgetExtension/NetBirdWidget.swiftNetBirdWidgetExtension/NetBirdWidgetExtension.entitlementsNetBirdWidgetExtension/NetBirdWidgetExtensionBundle.swiftNetBirdWidgetExtension/ToggleVPNIntent.swiftNetBirdWidgetExtension/VPNStatusProvider.swiftNetBirdWidgetExtension/Views/MediumWidgetView.swiftNetBirdWidgetExtension/Views/SmallWidgetView.swiftNetBirdWidgetExtension/Views/StatusIndicator.swiftNetBirdWidgetExtension/Views/WidgetActionButton.swiftNetBirdWidgetExtension/WidgetConstants.swiftNetBirdWidgetExtension/WidgetVPNStatus.swiftNetbirdKit/GlobalConstants.swift
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
NetBirdWidgetExtension/VPNStatusProvider.swift (1)
39-48:⚠️ Potential issue | 🟠 MajorThis still drops the persisted transition state on immediate reloads.
The connect/disconnect intents write
.connecting/.disconnectingintoWidgetConstants.keyVPNStatusbefore reloading timelines, but this branch still ignores that persisted value whenever a manager exists. The widget can therefore still snap back to the previous stable state right after a tap instead of showing the in-flight transition.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@NetBirdWidgetExtension/VPNStatusProvider.swift` around lines 39 - 48, The branch that chooses status from the NE manager ignores a persisted transition state; modify the NETunnelProviderManager.loadAllFromPreferences completion so that when a manager exists you first read the persisted raw value from WidgetConstants.keyVPNStatus (via defaults?.string) and, if it maps to WidgetVPNStatus.connecting or .disconnecting, use that persisted WidgetVPNStatus instead of always using WidgetVPNStatus(neStatus: manager.connection.status); otherwise fall back to constructing WidgetVPNStatus from manager.connection.status as currently done.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@NetBirdWidgetExtension/Intents/VPNIntentHelpers.swift`:
- Around line 22-39: Change waitForStableState(manager:) to return the resolved
WidgetVPNStatus instead of void: await the manager until timeout like it
currently does, compute final = WidgetVPNStatus(neStatus:
manager.connection.status), persist it (defaults?.set(...)) and then return
final. Update callers (ConnectVPNIntent.perform() and
DisconnectVPNIntent.perform()) to await the returned WidgetVPNStatus and use it
to decide success/error dialogs instead of hard-coded success; keep
WidgetCenter.shared.reloadAllTimelines() behavior after persisting. Ensure the
function signature and all call sites are updated accordingly.
In `@NetBirdWidgetExtension/ToggleVPNIntent.swift`:
- Around line 5-17: The ToggleVPNIntent currently exposes an unconstrained
String parameter action which can be any value but only "connect" is handled;
either make ToggleVPNIntent non-discoverable to Shortcuts by setting static var
isDiscoverable = false (so users use ConnectVPNIntent/DisconnectVPNIntent
instead), or restrict/validate the action parameter inside ToggleVPNIntent
(e.g., accept an enum-like closed set and validate action in init or when
running, mapping unknown values to a safe default or returning an error) and
update related code paths that read action to only act on validated values.
---
Duplicate comments:
In `@NetBirdWidgetExtension/VPNStatusProvider.swift`:
- Around line 39-48: The branch that chooses status from the NE manager ignores
a persisted transition state; modify the
NETunnelProviderManager.loadAllFromPreferences completion so that when a manager
exists you first read the persisted raw value from WidgetConstants.keyVPNStatus
(via defaults?.string) and, if it maps to WidgetVPNStatus.connecting or
.disconnecting, use that persisted WidgetVPNStatus instead of always using
WidgetVPNStatus(neStatus: manager.connection.status); otherwise fall back to
constructing WidgetVPNStatus from manager.connection.status as currently done.
🪄 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: 06d8379a-436c-4b14-a1d9-e9783aa46639
📒 Files selected for processing (13)
NetBird.xcodeproj/project.pbxprojNetBird/Source/App/NetBirdApp.swiftNetBird/Source/App/Views/iOS/iOSConnectionView.swiftNetBirdWidgetExtension/Intents/ConnectVPNIntent.swiftNetBirdWidgetExtension/Intents/DisconnectVPNIntent.swiftNetBirdWidgetExtension/Intents/NetBirdShortcuts.swiftNetBirdWidgetExtension/Intents/VPNIntentHelpers.swiftNetBirdWidgetExtension/Intents/VPNStatusIntent.swiftNetBirdWidgetExtension/ToggleVPNIntent.swiftNetBirdWidgetExtension/VPNStatusProvider.swiftNetBirdWidgetExtension/Views/WidgetActionButton.swiftNetBirdWidgetExtension/WidgetConstants.swiftNetBirdWidgetExtension/WidgetVPNStatus.swift
✅ Files skipped from review due to trivial changes (3)
- NetBirdWidgetExtension/WidgetConstants.swift
- NetBird.xcodeproj/project.pbxproj
- NetBird/Source/App/NetBirdApp.swift
🚧 Files skipped from review as they are similar to previous changes (1)
- NetBirdWidgetExtension/WidgetVPNStatus.swift
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@NetBird.xcodeproj/project.pbxproj`:
- Around line 202-207: PBXContainerItemProxy entry 55D865962F70982100A2EFF8 is
missing the remoteGlobalIDString field required by the dependency; update the
PBXContainerItemProxy object for that proxy (the one with remoteInfo =
NetBirdWidgetExtensionExtension) to include a remoteGlobalIDString value that
points to the widget extension target’s global ID (the same ID used by other
PBXContainerItemProxy entries referencing the widget extension), ensuring the
key name is exactly remoteGlobalIDString and the proxy retains containerPortal
and proxyType unchanged.
In `@NetBirdWidgetExtension/Intents/ConnectVPNIntent.swift`:
- Around line 27-33: The code sets
VPNIntentHelpers.defaults?.set(WidgetVPNStatus.connecting.rawValue, forKey:
WidgetConstants.keyVPNStatus) before calling startVPNTunnel(), but if
NETunnelProviderSession.startVPNTunnel() throws the widget stays in .connecting;
wrap the startVPNTunnel() call in a do/catch (or handle the thrown error), and
in the error path reset the shared status key
(VPNIntentHelpers.defaults?.set(..., forKey: WidgetConstants.keyVPNStatus)) back
to the non-connecting state (e.g., WidgetVPNStatus.disconnected.rawValue or the
previous value) and call WidgetCenter.shared.reloadAllTimelines() before
returning the failure; keep waitForStableState(manager:) only on success. Ensure
references: VPNIntentHelpers.defaults, WidgetConstants.keyVPNStatus,
WidgetCenter.shared.reloadAllTimelines(),
NETunnelProviderSession.startVPNTunnel(), and
VPNIntentHelpers.waitForStableState(manager:).
In `@NetBirdWidgetExtension/Intents/VPNIntentHelpers.swift`:
- Around line 23-35: waitForStableState(manager:) currently returns the first
stable status it sees which can be the same as the starting state; change it to
remember the initial state (let initial = manager.connection.status) and first
loop waiting for a state change (skip accepting a stable result until
manager.connection.status != initial), then once a different state is observed
continue polling until WidgetVPNStatus(neStatus:
manager.connection.status).isStable is true (or deadline/login handling
triggers) and then return that stable status; keep existing login check
(isLoginRequired → WidgetCenter.reloadAllTimelines and return) and existing
timeout behavior using WidgetConstants.pollInterval and
WidgetConstants.pollTimeout.
- Around line 29-40: When the isLoginRequired early-exit branch runs it reloads
timelines but doesn't update the persisted optimistic status, leaving the widget
stuck on "Connecting"; modify that branch in VPNIntentHelpers.swift so it writes
the current status into defaults before calling
WidgetCenter.shared.reloadAllTimelines() and returning—use
WidgetVPNStatus(neStatus: manager.connection.status).rawValue and
WidgetConstants.keyVPNStatus to persist the value so the stored optimistic
`.connecting` is cleared.
In `@NetBirdWidgetExtension/Views/MediumWidgetView.swift`:
- Around line 12-24: The CTA uses only entry.isConnected, causing incorrect
target/actions during transitional states; update the logic in MediumWidgetView
where WidgetActionButton (and the iOS 15/16 Link fallback) is chosen so it
branches on entry.status and entry.needsAppSetup instead of the boolean
isConnected; specifically, use entry.status values (.connecting, .disconnecting,
etc.) and entry.needsAppSetup to select the correct action/URL from
WidgetConstants (including the dedicated login/setup deep links) and keep the
existing transitionIndicator and iconLabel(isConnected:) usage but ensure they
reflect the full state when deciding which WidgetConstants.deepLink* to use.
In `@NetBirdWidgetExtension/Views/SmallWidgetView.swift`:
- Around line 23-27: The pre-iOS17 Link selection in SmallWidgetView.swift
currently only checks entry.isConnected and can route users incorrectly; update
the Link destination logic to mirror WidgetActionButton.swift by first checking
entry.needsAppSetup && !entry.isConnected and selecting entry.loginRequired ?
WidgetConstants.deepLinkLogin : WidgetConstants.deepLinkConnect, otherwise fall
back to entry.isConnected ? WidgetConstants.deepLinkDisconnect :
WidgetConstants.deepLinkConnect so setup/login routing matches for iOS 15/16
users.
🪄 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: e3bc1e41-34fa-4594-8299-1dab27d0eeed
📒 Files selected for processing (12)
NetBird.xcodeproj/project.pbxprojNetBird/Source/App/NetBirdApp.swiftNetBird/Source/App/ViewModels/MainViewModel.swiftNetBirdWidgetExtension/Intents/ConnectVPNIntent.swiftNetBirdWidgetExtension/Intents/DisconnectVPNIntent.swiftNetBirdWidgetExtension/Intents/VPNIntentHelpers.swiftNetBirdWidgetExtension/NetBirdWidget.swiftNetBirdWidgetExtension/NetBirdWidgetExtension.entitlementsNetBirdWidgetExtension/VPNStatusProvider.swiftNetBirdWidgetExtension/Views/MediumWidgetView.swiftNetBirdWidgetExtension/Views/SmallWidgetView.swiftNetBirdWidgetExtension/WidgetConstants.swift
✅ Files skipped from review due to trivial changes (2)
- NetBirdWidgetExtension/NetBirdWidgetExtension.entitlements
- NetBirdWidgetExtension/WidgetConstants.swift
🚧 Files skipped from review as they are similar to previous changes (4)
- NetBird/Source/App/NetBirdApp.swift
- NetBirdWidgetExtension/Intents/DisconnectVPNIntent.swift
- NetBirdWidgetExtension/NetBirdWidget.swift
- NetBird/Source/App/ViewModels/MainViewModel.swift
Summary by CodeRabbit
New Features
Chores
Bug Fixes