Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable switching of networks and server #158

Merged
merged 61 commits into from
Jan 30, 2025
Merged

Conversation

danielnordh
Copy link
Collaborator

@danielnordh danielnordh commented Jan 27, 2025

This PR enables network and server switching after onboarding.

  • Introduces WalletClient class that holds lightningClient, appState and create/start/restart/delete wallet functions
  • Uses NetworkSettingsViewModel for both onboarding and settings purposes
  • Adds .restart() and .reset() functions to LightningClient/service as necessary
  • Un-refactors SettingsView again
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-01-28.at.14.31.15.mp4

Summary by CodeRabbit

Based on the comprehensive changes across multiple files, here are the updated release notes:

  • New Features

    • Introduced a new WalletClient to manage wallet operations.
    • Enhanced wallet management with improved state handling.
    • Added robust network and server configuration options with user confirmation.
  • Improvements

    • Streamlined application state management by replacing AppState with WalletClient.
    • Improved error handling for wallet and network operations.
    • Updated Lightning Node service with a thread-safe singleton pattern.
  • Changes

    • Modified view models and views to utilize WalletClient for state management.
    • Updated initialization and data flow in various views to reflect changes in state management.
  • Bug Fixes

    • Improved concurrency and asynchronous operation handling.
    • Enhanced thread safety in the Lightning Node service.
    • Resolved issues related to UI updates and data binding.
  • Dependency Updates

    • Updated ldk-node dependency from version 0.4.2 to 0.4.3.

danielnordh and others added 30 commits January 16, 2025 19:23
Refactor

Co-authored-by: Matthew Ramsden <6657488+reez@users.noreply.github.com>
@reez
Copy link
Owner

reez commented Jan 29, 2025

added some questions before I kick off a deeper code review

Everything worked testing on my end!

testing-pr

Copy link
Contributor

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

🧹 Nitpick comments (6)
LDKNodeMonday/View/Home/OnboardingView.swift (2)

74-84: Consider adding loading state during wallet creation.

The wallet creation process is asynchronous but there's no visual feedback to the user during this operation.

 Button("Create wallet") {
+    @State private var isCreatingWallet = false
     Task {
+        isCreatingWallet = true
         await viewModel.saveSeed()
+        isCreatingWallet = false
     }
 }
 .buttonStyle(
     BitcoinFilled(
         tintColor: .accent,
-        isCapsule: true
+        isCapsule: true,
+        isLoading: isCreatingWallet
     )
 )

74-84: Consider adding loading state during wallet creation.

While the wallet creation logic is correct, the button remains interactive during the async operation.

 Button("Create wallet") {
+    @State private var isCreatingWallet = false
     Task {
+        isCreatingWallet = true
         await viewModel.saveSeed()
+        isCreatingWallet = false
     }
 }
 .buttonStyle(
     BitcoinFilled(
         tintColor: .accent,
         isCapsule: true
     )
 )
+.disabled(isCreatingWallet)
LDKNodeMonday/View/Home/NetworkSettingsView.swift (2)

13-16: Consider using an enum for temporary settings state.

The current implementation uses separate optional properties for temporary network and server. Consider using an enum to better represent the state.

+enum NetworkSettingsChange {
+    case network(Network)
+    case server(EsploraServer)
+    case both(network: Network, server: EsploraServer)
+}
+
 struct NetworkSettingsView: View {
     @Environment(\.dismiss) private var dismiss
     @Binding var walletClient: WalletClient
     @State private var showRestartAlert = false
-    @State private var tempNetwork: Network?
-    @State private var tempServer: EsploraServer?
+    @State private var pendingChange: NetworkSettingsChange?

89-98: Consider moving settings persistence to WalletClient.

The settings persistence logic could be encapsulated in the WalletClient for better separation of concerns.

-do {
-    try KeyClient.live.saveNetwork(newNetwork.description)
-    try KeyClient.live.saveServerURL(newServer.url)
-} catch let error {
-    await MainActor.run {
-        debugPrint(error.localizedDescription)
-        walletClient.appError = error
-        walletClient.appState = .error
-    }
-}
+await walletClient.saveNetworkSettings(network: newNetwork, server: newServer)
LDKNodeMonday/App/LDKNodeMondayApp.swift (2)

Line range hint 26-46: Consider handling all app states explicitly.

The default case in the switch statement might hide state handling issues. Consider explicitly handling all possible states of walletClient.appState to make the state transitions more obvious and maintainable.

-                switch walletClient.appState {
-                case .onboarding:
-                    OnboardingView(...)
-                case .wallet:
-                    BitcoinView(...)
-                case .error:
-                    ErrorView(error: self.appError)
-                default:
-                    LoadingView()
-                }
+                switch walletClient.appState {
+                case .onboarding:
+                    OnboardingView(...)
+                case .wallet:
+                    BitcoinView(...)
+                case .error:
+                    ErrorView(error: self.appError)
+                case .loading:
+                    LoadingView()
+                }

59-64: Consider handling errors in applicationWillTerminate.

The stop() call should include error handling to ensure graceful shutdown even if the operation fails.

     func applicationWillTerminate(_ application: UIApplication) {
-        walletClient?.stop()
+        do {
+            try walletClient?.stop()
+        } catch {
+            print("Failed to stop wallet client: \(error)")
+        }
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca99376 and ddd97dc.

📒 Files selected for processing (6)
  • LDKNodeMonday.xcodeproj/project.pbxproj (5 hunks)
  • LDKNodeMonday/App/LDKNodeMondayApp.swift (2 hunks)
  • LDKNodeMonday/View Model/Home/OnboardingViewModel.swift (1 hunks)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift (1 hunks)
  • LDKNodeMonday/View/Home/OnboardingView.swift (3 hunks)
  • LDKNodeMonday/View/Settings/SettingsView.swift (2 hunks)
🧰 Additional context used
📓 Learnings (3)
LDKNodeMonday/App/LDKNodeMondayApp.swift (2)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app's `WalletClient.delete()` function, errors during wallet deletion are handled through a do-catch block that updates `appError` with the caught error and sets `appState` to `.error`, with all state updates properly dispatched to the main actor.
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app, errors during wallet deletion are handled directly within the `WalletClient.delete()` function by updating `appError` and `appState`.
LDKNodeMonday/View Model/Home/OnboardingViewModel.swift (3)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View Model/Home/OnboardingViewModel.swift:35-40
Timestamp: 2025-01-28T15:47:01.983Z
Learning: In the Monday app, errors during wallet creation are handled directly in the WalletClient.createWallet() function by updating appError and appState, rather than requiring error handling at the call site.
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Home/ImportWalletView.swift:57-59
Timestamp: 2025-01-28T15:50:23.744Z
Learning: In the Monday app, error handling for wallet import is implemented in the WalletClient's createWallet() function, which updates appError and appState when an error occurs. The OnboardingViewModel's saveSeed() function delegates error handling to createWallet().
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app, errors during wallet deletion are handled directly within the `WalletClient.delete()` function by updating `appError` and `appState`.
LDKNodeMonday/View/Settings/SettingsView.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app's `WalletClient.delete()` function, errors during wallet deletion are handled through a do-catch block that updates `appError` with the caught error and sets `appState` to `.error`, with all state updates properly dispatched to the main actor.
🔇 Additional comments (18)
LDKNodeMonday.xcodeproj/project.pbxproj (1)

916-916: Verify the package version downgrade.

The ldk-node package version has been downgraded from 0.4.3 to 0.4.2. This change needs to be reviewed to ensure it doesn't introduce compatibility issues or security vulnerabilities.

Let's verify the package versions and any associated security advisories:

#!/bin/bash
# Description: Check for security advisories and latest versions of the ldk-node package

# Check GitHub releases for latest versions
gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  /repos/lightningdevkit/ldk-node/releases | jq -r '.[].tag_name' | head -n 5

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: RUST, package: "ldk-node") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

[skip_cloning]

LDKNodeMonday/View Model/Home/OnboardingViewModel.swift (6)

12-13: LGTM! Property declarations look good.

The transition from direct state management to using WalletClient improves encapsulation and separation of concerns.


22-24: LGTM! Clean initialization.

The initialization is properly handling the binding unwrapping and setting up the required properties.


27-32: Verify error propagation in createWallet.

Based on the retrieved learnings, errors during wallet creation are handled in WalletClient.createWallet(). However, let's verify the implementation to ensure proper error handling.

✅ Verification successful

Error handling in createWallet is properly implemented

The method has comprehensive error handling with errors being caught and properly propagated through WalletClient's state management. The OnboardingViewModel doesn't need additional error handling as it can observe WalletClient's state changes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find the implementation of createWallet in WalletClient
ast-grep --pattern 'func createWallet(seedPhrase: String, network: Network, server: EsploraServer) async {
  $$$
}'

Length of output: 1344


12-13: LGTM! Clean property declarations.

The transition from appState to walletClient binding improves encapsulation by centralizing wallet management.


22-24: LGTM! Constructor properly initializes dependencies.

The initialization is clean and correctly sets up the lightningClient from the walletClient.


27-32: LGTM! Wallet creation is properly delegated.

Based on the retrieved learnings, error handling is correctly implemented in the WalletClient's createWallet() function.

LDKNodeMonday/View/Home/OnboardingView.swift (4)

31-48: LGTM! Clean network settings implementation.

The network settings button and sheet presentation are well-implemented with proper state management through WalletClient.


112-116: LGTM! Preview configuration is correct.

The preview is properly configured with mock dependencies.


31-44: LGTM! Network settings button implementation.

Clean implementation with proper sheet presentation and network display.


112-116: LGTM! Preview uses appropriate mock client.

The preview correctly initializes the view model with a mock WalletClient.

LDKNodeMonday/View/Home/NetworkSettingsView.swift (3)

89-98: LGTM! Proper error handling for network settings.

The error handling for saving network settings is well-implemented with proper main actor dispatching.


13-16: LGTM! State management for network changes.

Temporary state variables effectively handle the transition between selection and confirmation.


25-40: LGTM! Network selection with proper state handling.

The implementation correctly differentiates between onboarding and runtime behavior.

LDKNodeMonday/App/LDKNodeMondayApp.swift (2)

19-21: LGTM! Clean initialization of WalletClient.

The initialization properly sets up the wallet client in the shared AppDelegate instance.


29-38: Verify view model bindings across the app.

The view models are initialized with walletClient binding. Let's verify that all view models consistently handle the wallet client binding.

✅ Verification successful

View model bindings are consistently implemented

All view models (BitcoinViewModel, SettingsViewModel, OnboardingViewModel) consistently use @Binding var walletClient: WalletClient with proper initialization patterns. Preview/mock implementations also follow the same pattern using .constant(WalletClient(...)).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for view model initializations with walletClient
ast-grep --pattern 'init(walletClient: $_)'

Length of output: 445


Script:

#!/bin/bash
# Find all view model files
echo "=== View Model Files ==="
fd "ViewModel.swift|View.swift"

echo -e "\n=== WalletClient Usage ==="
rg "WalletClient" -A 2

echo -e "\n=== Binding Usage in View Models ==="
rg "Binding<" -A 2

Length of output: 9588

LDKNodeMonday/View/Settings/SettingsView.swift (2)

33-39: LGTM! Network settings navigation with current network display.

Clean implementation showing the current network state in the badge.


117-122: LGTM! Wallet deletion with proper error handling.

Based on the retrieved learning, error handling is correctly implemented in WalletClient.delete().

Copy link
Contributor

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

♻️ Duplicate comments (1)
LDKNodeMonday/View/Home/NetworkSettingsView.swift (1)

30-33: ⚠️ Potential issue

Replace fatalError with proper error handling.

Using fatalError in production code can lead to app crashes. This should be handled gracefully.

-    guard let server = availableServers(network: newNetwork).first else {
-        // This should never happen, but if it does:
-        fatalError("No servers available for \(newNetwork)")
-    }
+    guard let server = availableServers(network: newNetwork).first else {
+        walletClient.appError = MondayError.custom(message: "No servers available for \(newNetwork)")
+        walletClient.appState = .error
+        return
+    }
🧹 Nitpick comments (3)
LDKNodeMonday/View/Home/NetworkSettingsView.swift (3)

13-16: Consider using a dedicated type for managing temporary network settings.

The current implementation uses separate state variables for temporary values. Consider encapsulating these in a dedicated type for better state management.

-    @State private var tempNetwork: Network?
-    @State private var tempServer: EsploraServer?
+    @State private var pendingSettings: NetworkSettings?
+
+    struct NetworkSettings {
+        let network: Network
+        let server: EsploraServer
+    }

89-98: Improve error handling specificity.

The error handling could be more specific about what went wrong during the save operation.

     do {
         try KeyClient.live.saveNetwork(newNetwork.description)
         try KeyClient.live.saveServerURL(newServer.url)
-    } catch let error {
+    } catch let error as KeyClientError {
         await MainActor.run {
-            debugPrint(error.localizedDescription)
+            let errorMessage = "Failed to save network settings: \(error.localizedDescription)"
+            debugPrint(errorMessage)
+            walletClient.appError = MondayError.custom(message: errorMessage)
             walletClient.appState = .error
         }
+    } catch {
+        await MainActor.run {
+            let errorMessage = "Unexpected error while saving network settings"
+            debugPrint(error.localizedDescription)
+            walletClient.appError = MondayError.custom(message: errorMessage)
+            walletClient.appState = .error
+        }
     }

80-105: Consider using structured concurrency for better task management.

The current implementation could benefit from structured concurrency patterns for better error handling and task cancellation.

-    if tempNetwork != nil || tempServer != nil {
-        Task {
+    if let newNetwork = tempNetwork ?? walletClient.network {
+        Task {
+            try? await withThrowingTaskGroup(of: Void.self) { group in
+                group.addTask {
+                    try await saveNetworkSettings(
+                        network: newNetwork,
+                        server: tempServer ?? availableServers(network: newNetwork).first
+                    )
+                }
+                
+                group.addTask {
+                    await walletClient.restart(
+                        newNetwork: newNetwork,
+                        newServer: tempServer ?? availableServers(network: newNetwork).first
+                    )
+                }
+            }
+        }
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddd97dc and 8ffd850.

📒 Files selected for processing (1)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift (1 hunks)
🔇 Additional comments (2)
LDKNodeMonday/View/Home/NetworkSettingsView.swift (2)

50-66: Well-structured server picker implementation!

The server picker implementation is clean and follows SwiftUI best practices:

  • Proper binding usage
  • Correct state management for both onboarding and settings changes
  • Dynamic server list generation based on the current network

83-86: ⚠️ Potential issue

Replace fatalError with proper error handling in the restart flow.

Similar to the previous instance, using fatalError should be avoided.

-    guard let server = availableServers(network: newNetwork).first else {
-        // This should never happen, but if it does:
-        fatalError("No servers available for \(newNetwork)")
-    }
+    guard let server = availableServers(network: newNetwork).first else {
+        await MainActor.run {
+            walletClient.appError = MondayError.custom(message: "No servers available for \(newNetwork)")
+            walletClient.appState = .error
+        }
+        return
+    }

Likely invalid or redundant comment.

@reez
Copy link
Owner

reez commented Jan 30, 2025

@danielnordh code looks good so no hold up there.

1 last thing is seeing if you can reproduce what I think is a bug?

Reproduce:

  • change server (works great)
  • kill app then open app, goes back to default server instead of the one just chosen before killing app
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-01-29.at.19.46.06.mp4

@danielnordh
Copy link
Collaborator Author

  • change server (works great)
  • kill app then open app, goes back to default server instead of the one just chosen before killing app

Very good catch. Fixed in 635a7dd

Copy link
Contributor

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

🧹 Nitpick comments (10)
LDKNodeMonday/App/LDKNodeMondayApp.swift (2)

25-42: Consider encapsulating lightningClient access.

While the view hierarchy is well-structured, directly accessing walletClient.lightningClient might expose internal implementation details. Consider providing a dedicated method or property wrapper in WalletClient for lightning-related operations.

Example approach:

-lightningClient: walletClient.lightningClient
+lightningClient: walletClient.getLightningService()

59-62: Add nil check for safer cleanup.

While the cleanup is handled correctly, adding a nil check would make the code more robust.

-    var walletClient: WalletClient?
+    weak var walletClient: WalletClient?

     func applicationWillTerminate(_ application: UIApplication) {
-        walletClient?.stop()
+        if let client = walletClient {
+            client.stop()
+        }
     }

Also consider marking the walletClient property as weak to avoid potential retain cycles.

LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (2)

14-30: Consider improving thread safety and nil handling.

The current singleton implementation could be enhanced:

  1. Replace force unwrapping with optional binding
  2. Use OSAllocatedUnfairLock (iOS 16+) or dispatch_once pattern for better performance
  3. Add memory barrier between check and initialization
-    private static let lock = NSLock()
-    private static var _shared: LightningNodeService?
-    static var shared: LightningNodeService {
-        get {
-            lock.lock()
-            defer { lock.unlock() }
-            if _shared == nil {
-                _shared = LightningNodeService()
-            }
-            return _shared!
-        }
-        set {
-            lock.lock()
-            _shared = newValue
-            lock.unlock()
-        }
-    }
+    private static let lock = OSAllocatedUnfairLock()
+    private static var _shared: LightningNodeService?
+    static var shared: LightningNodeService {
+        get {
+            lock.withLock {
+                if _shared == nil {
+                    _shared = LightningNodeService()
+                }
+                guard let instance = _shared else {
+                    fatalError("Failed to initialize LightningNodeService")
+                }
+                return instance
+            }
+        }
+        set {
+            lock.withLock {
+                _shared = newValue
+            }
+        }
+    }

433-433: Enhance mock implementation for better testing.

The mock implementation of listenForEvents is empty. Consider simulating events for better test coverage.

-            listenForEvents: {}
+            listenForEvents: {
+                NotificationCenter.default.post(
+                    name: .ldkEventReceived,
+                    object: "MOCK_EVENT: Channel opened"
+                )
+            }

Also applies to: 489-492

LDKNodeMonday/View/Settings/SettingsView.swift (3)

24-42: LGTM! Consider enhancing accessibility for network badge.

The Wallet section implementation looks good with proper integration of WalletClient for network management.

Consider adding an accessibility label for the network badge to improve VoiceOver experience:

 Label("Network", systemImage: "network")
     .badge((viewModel.network ?? "No network").capitalized)
+    .accessibilityLabel("Network: \((viewModel.network ?? "No network").capitalized)")

54-67: Enhance copy functionality with better feedback and error handling.

The copy button implementation could be improved for better user experience and reliability.

Consider these enhancements:

 Button {
+    do {
         UIPasteboard.general.string = viewModel.nodeID
+        UINotificationFeedbackGenerator().notificationOccurred(.success)
         showCheckmark = true
-        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
             showCheckmark = false
         }
+    } catch {
+        UINotificationFeedbackGenerator().notificationOccurred(.error)
+    }
 } label: {

167-173: Enhance preview with documentation and multiple states.

Consider adding multiple preview states to help visualize different scenarios.

Here's a suggested enhancement:

 #if DEBUG
+    /// Preview states for SettingsView
     #Preview {
-        SettingsView(
-            viewModel: .init(
-                walletClient: .constant(WalletClient(keyClient: KeyClient.mock)),
-                lightningClient: .mock
-            )
-        )
+        Group {
+            // Normal state
+            SettingsView(
+                viewModel: .init(
+                    walletClient: .constant(WalletClient(keyClient: KeyClient.mock)),
+                    lightningClient: .mock
+                )
+            )
+            .previewDisplayName("Normal State")
+            
+            // Loading state
+            SettingsView(
+                viewModel: .init(
+                    walletClient: .constant(WalletClient(keyClient: KeyClient.mock)),
+                    lightningClient: .mock,
+                    isStoppingNode: true
+                )
+            )
+            .previewDisplayName("Loading State")
+        }
     }
 #endif
LDKNodeMonday/View/Home/NetworkSettingsView.swift (3)

13-16: Consider renaming temporary state variables for clarity.

While the temporary state pattern was previously approved, consider renaming these variables to better reflect their purpose:

-    @State private var tempNetwork: Network?
-    @State private var tempServer: EsploraServer?
+    @State private var pendingNetwork: Network?
+    @State private var pendingServer: EsploraServer?

25-41: Extract network selection logic for better readability.

The binding logic is complex and could be moved to a separate method for better maintainability.

+    private func handleNetworkSelection(_ newNetwork: Network) {
+        if walletClient.appState == .onboarding {
+            walletClient.network = newNetwork
+            guard let server = availableServers(network: newNetwork).first else {
+                fatalError("No servers available for \(newNetwork)")
+            }
+            walletClient.server = server
+        } else {
+            tempNetwork = newNetwork
+            showRestartAlert = true
+        }
+    }
+
     Picker(
         "Network",
-        selection: Binding(
-            get: { walletClient.network },
-            set: { newNetwork in
-                if walletClient.appState == .onboarding {
-                    walletClient.network = newNetwork
-                    guard let server = availableServers(network: newNetwork).first else {
-                        fatalError("No servers available for \(newNetwork)")
-                    }
-                    walletClient.server = server
-                } else {
-                    tempNetwork = newNetwork
-                    showRestartAlert = true
-                }
-            }
-        )
+        selection: Binding(
+            get: { walletClient.network },
+            set: handleNetworkSelection
+        )

51-61: Extract server selection logic for consistency.

For consistency with the network picker refactor, the server selection logic should also be extracted.

+    private func handleServerSelection(_ newServer: EsploraServer) {
+        if walletClient.appState == .onboarding {
+            walletClient.server = newServer
+        } else {
+            tempServer = newServer
+            showRestartAlert = true
+        }
+    }
+
     Picker(
         "Server",
-        selection: Binding(
-            get: { walletClient.server },
-            set: { newServer in
-                if walletClient.appState == .onboarding {
-                    walletClient.server = newServer
-                } else {
-                    tempServer = newServer
-                    showRestartAlert = true
-                }
-            }
-        )
+        selection: Binding(
+            get: { walletClient.server },
+            set: handleServerSelection
+        )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfb0631 and 635a7dd.

📒 Files selected for processing (5)
  • LDKNodeMonday.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (1 hunks)
  • LDKNodeMonday/App/LDKNodeMondayApp.swift (1 hunks)
  • LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (9 hunks)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift (1 hunks)
  • LDKNodeMonday/View/Settings/SettingsView.swift (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • LDKNodeMonday.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
🧰 Additional context used
📓 Learnings (3)
LDKNodeMonday/View/Settings/SettingsView.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app's `WalletClient.delete()` function, errors during wallet deletion are handled through a do-catch block that updates `appError` with the caught error and sets `appState` to `.error`, with all state updates properly dispatched to the main actor.
LDKNodeMonday/App/LDKNodeMondayApp.swift (3)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/App/LDKNodeMondayApp.swift:47-52
Timestamp: 2025-01-29T19:03:13.205Z
Learning: Error handling for wallet operations (start, create, restart, delete) is managed within the WalletClient class, which updates both appError and appState properties when errors occur. Additional error handling in LDKNodeMondayApp is not needed.
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app, errors during wallet deletion are handled directly within the `WalletClient.delete()` function by updating `appError` and `appState`.
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Settings/SettingsView.swift:118-123
Timestamp: 2025-01-28T15:46:14.253Z
Learning: In the LDKNodeMonday app's `WalletClient.delete()` function, errors during wallet deletion are handled through a do-catch block that updates `appError` with the caught error and sets `appState` to `.error`, with all state updates properly dispatched to the main actor.
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift:150-155
Timestamp: 2025-01-28T15:52:09.651Z
Learning: In LightningNodeService.swift, cleanup operations like deleteDocuments() and deleteWallet() are intentionally kept as separate functions rather than being coupled with reset() to allow more granular control over when cleanup happens.
🔇 Additional comments (4)
LDKNodeMonday/App/LDKNodeMondayApp.swift (2)

15-19: LGTM! Clean state management setup.

The initialization of WalletClient and its integration with AppDelegate follows SwiftUI best practices for state management and dependency injection.


47-51: LGTM! Clean state change handling.

The implementation properly handles navigation path resets on state changes and wallet initialization follows SwiftUI's task pattern. Error handling is correctly delegated to WalletClient as per the established pattern.

LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (1)

149-162: Implementation looks good!

The restart and reset methods are well-implemented with proper error handling and clear separation of concerns. The decision to keep cleanup operations separate (as mentioned in the learnings) provides good flexibility.

LDKNodeMonday/View/Home/NetworkSettingsView.swift (1)

128-128: LGTM! Preview implementation follows best practices.

The preview correctly uses a mock KeyClient for testing.

Copy link
Contributor

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

♻️ Duplicate comments (1)
LDKNodeMonday/View/Home/NetworkSettingsView.swift (1)

25-41: ⚠️ Potential issue

Replace fatalError with proper error handling.

Based on the established guidelines for the Monday app, fatal errors should be avoided in production code.

-                                    guard let server = availableServers(network: newNetwork).first
-                                    else {
-                                        // This should never happen, but if it does:
-                                        fatalError("No servers available for \(newNetwork)")
-                                    }
+                                    guard let server = availableServers(network: newNetwork).first
+                                    else {
+                                        walletClient.appError = MondayError.custom(message: "No servers available for \(newNetwork)")
+                                        walletClient.appState = .error
+                                        return
+                                    }
🧹 Nitpick comments (2)
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (2)

41-52: Consider using Result type for error handling.

The current implementation uses optional try and force unwrapping which could lead to runtime crashes.

Consider using a Result type for more robust error handling:

-        if let backupInfo = try? KeyClient.live.getBackupInfo() {
-            guard let network = Network(stringValue: backupInfo.networkString) else {
-                fatalError("Configuration error: No Network found in BackupInfo")
-            }
+        let result = Result { try KeyClient.live.getBackupInfo() }
+        switch result {
+        case .success(let backupInfo):
+            guard let network = Network(stringValue: backupInfo.networkString) else {
+                logger.error("Invalid network string: \(backupInfo.networkString)")
+                self.network = .signet
+                self.server = .mutiny_signet
+                return
+            }

333-337: Consider documenting the LightningNodeClient interface.

The public interface looks good, but could benefit from documentation explaining the purpose of each method, especially the new ones.

Add documentation for the new methods:

 public struct LightningNodeClient {
+    /// Starts the Lightning node service
     let start: () async throws -> Void
+    /// Stops the Lightning node service
     let stop: () throws -> Void
+    /// Restarts the Lightning node service by stopping it, resetting the shared instance, and starting it again
     let restart: () async throws -> Void
+    /// Resets the Lightning node service by stopping it and clearing the shared instance
     let reset: () throws -> Void

Also applies to: 361-361, 370-371, 433-435, 444-445, 491-493

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 635a7dd and f4f2300.

📒 Files selected for processing (2)
  • LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (9 hunks)
  • LDKNodeMonday/View/Home/NetworkSettingsView.swift (1 hunks)
🧰 Additional context used
📓 Learnings (2)
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift:150-155
Timestamp: 2025-01-28T15:52:09.651Z
Learning: In LightningNodeService.swift, cleanup operations like deleteDocuments() and deleteWallet() are intentionally kept as separate functions rather than being coupled with reset() to allow more granular control over when cleanup happens.
LDKNodeMonday/View/Home/NetworkSettingsView.swift (1)
Learnt from: danielnordh
PR: reez/Monday#158
File: LDKNodeMonday/View/Home/NetworkSettingsView.swift:30-33
Timestamp: 2025-01-29T19:07:21.325Z
Learning: In the Monday app, fatal errors should be avoided in production code. Instead, errors should be presented to users by setting walletClient.appError and transitioning to walletClient.appState = .error.
🔇 Additional comments (7)
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (3)

14-30: Thread-safe singleton implementation looks good.

The implementation correctly uses NSLock to ensure thread safety when accessing the shared instance.


159-164: LGTM: reset() implementation aligns with design.

The reset() method is intentionally kept separate from cleanup operations as learned from previous feedback.


151-157: Verify restart() implementation.

The restart() method looks good but let's verify its usage in the codebase to ensure all callers handle the async/throws correctly.

✅ Verification successful

restart() implementation and usage verified successfully

All callers properly handle the async/throws nature of the method with correct error propagation and state management.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for restart() calls to verify error handling
rg -A 5 'restart\(\)' --type swift

Length of output: 1949

LDKNodeMonday/View/Home/NetworkSettingsView.swift (4)

13-16: LGTM! Property declarations are well-structured.

The binding to walletClient and state variables for managing temporary network settings are appropriate for this use case.


51-61: LGTM! Server picker binding is well-implemented.

The conditional logic properly handles both onboarding and post-onboarding scenarios.


77-86: LGTM! Alert implementation is clear and user-friendly.

The alert properly informs users about the restart requirement and provides clear actions.


63-66: Verify handling of empty server list.

Please ensure that the UI gracefully handles scenarios where availableServers returns an empty list.

@reez reez merged commit bd2d06d into reez:main Jan 30, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 3, 2025
7 tasks
@danielnordh danielnordh deleted the networkswitch branch February 13, 2025 15:13
@coderabbitai coderabbitai bot mentioned this pull request Feb 19, 2025
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