-
Notifications
You must be signed in to change notification settings - Fork 8
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
chore: lightningnodeclient #152
Conversation
WalkthroughThe changes introduce a new Changes
Possibly related PRs
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
🧹 Nitpick comments (3)
LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (3)
274-300
: LGTM! Consider improving parameter names for better clarity.The client interface is well-structured and provides good separation of concerns. However, some closure parameter names could be more descriptive:
- let connect: (PublicKey, String, Bool) async throws -> Void + let connect: (nodeId: PublicKey, address: String, persist: Bool) async throws -> Void - let connectOpenChannel: (PublicKey, String, UInt64, UInt64?, ChannelConfig?, Bool) async throws -> UserChannelId + let connectOpenChannel: (nodeId: PublicKey, address: String, channelAmountSats: UInt64, pushToCounterpartyMsat: UInt64?, channelConfig: ChannelConfig?, announceChannel: Bool) async throws -> UserChannelId - let receive: (UInt64, String, UInt32) async throws -> String + let receive: (amountSat: UInt64, message: String, expirySec: UInt32) async throws -> String
361-406
: Consider documenting mock values and using more realistic test data.The mock implementation is comprehensive but could benefit from:
- Using a real testnet/signet node ID for better test scenarios
- Adding comments explaining the chosen mock values
- Considering edge cases in mock responses
static let mock = Self( start: { }, stop: { }, - nodeId: { "038474837483784378437843784378437843784378" }, + // Real signet node ID for better test scenarios + nodeId: { "0269f78d9adf243864b0ebc137b4f0558f44d2262c9885e5b0ab5f23c1a6d6eec3" }, newAddress: { "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" }, - spendableOnchainBalanceSats: { 100_000 }, + // 100k sats = 0.001 BTC + spendableOnchainBalanceSats: { 100_000 }, // ... rest of the implementation )
274-406
: Add documentation for the client interface.Consider adding documentation comments to:
- The
LightningNodeClient
struct explaining its purpose and usage- Key properties that might need additional context
- Examples of how to use the live and mock implementations
+/// A client interface for interacting with the Lightning Node Service. +/// +/// Use the `live` instance for production code: +/// ```swift +/// let client = LightningNodeClient.live +/// try await client.start() +/// ``` +/// +/// For testing, use the `mock` instance: +/// ```swift +/// let client = LightningNodeClient.mock +/// ``` struct LightningNodeClient { let start: () async throws -> Void let stop: () throws -> Void
There was a problem hiding this 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
🔭 Outside diff range comments (1)
LDKNodeMonday/View/Home/OnboardingView.swift (1)
Update NetworkSettingsView and ImportWalletView to use @ObservedObject
Both views should use
@ObservedObject var viewModel: OnboardingViewModel
instead of@EnvironmentObject
to maintain consistency with:
- The parent OnboardingView
- Other views in the codebase that use
@ObservedObject
for their view models🔗 Analysis chain
Line range hint
38-39
: Consider updating child view implementations for consistency.The
NetworkSettingsView
andImportWalletView
are using the ViewModel as an environment object. Consider passing the required dependencies explicitly to maintain consistency with the new dependency injection pattern.Let's check if these views have been updated in other files:
Also applies to: 64-65
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check if NetworkSettingsView and ImportWalletView have been updated # to use the new dependency injection pattern # Search for NetworkSettingsView implementation echo "Checking NetworkSettingsView implementation:" rg -A 5 "struct NetworkSettingsView" # Search for ImportWalletView implementation echo "Checking ImportWalletView implementation:" rg -A 5 "struct ImportWalletView"Length of output: 1255
Script:
#!/bin/bash # Search for other view implementations to understand the new DI pattern echo "Checking view implementations in the Home directory:" rg -B 2 -A 3 "struct \w+View: View" "LDKNodeMonday/View/Home/" echo "\nChecking OnboardingView specifically:" rg -B 2 -A 5 "struct OnboardingView" "LDKNodeMonday/View/Home/"Length of output: 8297
🧹 Nitpick comments (34)
LDKNodeMonday/View/Settings/Peers/PeerDetailsView.swift (2)
89-90
: Consider extracting preview constants and validating node ID.While the addition of
lightningClient: .mock
is a good practice for preview testing, consider:
- Moving the node ID to a preview-specific constant
- Validating the node ID string at compile-time
+ private extension PeerDetailsView { + static let previewNodeId = "03e39c737a691931dac0f9f9ee803f2ab08f7fd3bbb25ec08d9b8fdb8f51d3a8db" + } #Preview { PeerDetailsView( viewModel: .init( - nodeId: "03e39c737a691931dac0f9f9ee803f2ab08f7fd3bbb25ec08d9b8fdb8f51d3a8db", + nodeId: PeerDetailsView.previewNodeId, lightningClient: .mock ) ) }
89-90
: Great architectural improvement!The addition of
lightningClient
parameter is a positive change that:
- Moves away from singleton pattern
- Improves testability through dependency injection
- Enables better preview functionality with mock client
LDKNodeMonday/View/Home/Receive/ReceiveView.swift (1)
Line range hint
146-152
: Update preview to reflect new dependencies.The preview section should be updated to include the required
lightningClient
. This will help maintain the preview's functionality and serve as documentation for other developers.#if DEBUG #Preview { - AmountEntryView( - amount: .constant("21") - ) + ReceiveView( + lightningClient: .mock() // Assuming there's a mock implementation + ) } #endifLDKNodeMonday/View Model/Home/Receive/JITInvoiceViewModel.swift (1)
57-57
: Consider enhancing the color retrieval method.While the implementation works, consider these improvements:
- Use async/await pattern for consistency with other methods
- Rename to something more descriptive like
updateNetworkColor()
Here's a suggested implementation:
- func getColor() { - let color = lightningClient.getNetworkColor() + func updateNetworkColor() async { + let color = await lightningClient.getNetworkColor() DispatchQueue.main.async { self.networkColor = color } }LDKNodeMonday/View Model/Profile/Channel/ChannelDetailViewModel.swift (1)
26-28
: Consider modernizing to async/await pattern.While the implementation is correct, this network operation would benefit from using Swift's modern concurrency pattern. This would make the asynchronous nature of the operation more explicit and eliminate the need for manual dispatch queue management in the error handling.
Example refactor:
- func close() { + @MainActor + func close() async throws { - do { - try lightningClient.closeChannel( - self.channel.userChannelId, - self.channel.counterpartyNodeId - ) - channelDetailViewError = nil - } catch let error as NodeError { - let errorString = handleNodeError(error) - DispatchQueue.main.async { - self.channelDetailViewError = .init( - title: errorString.title, - detail: errorString.detail - ) - } - } catch { - DispatchQueue.main.async { - self.channelDetailViewError = .init( - title: "Unexpected error", - detail: error.localizedDescription - ) - } - } + do { + try await lightningClient.closeChannel( + self.channel.userChannelId, + self.channel.counterpartyNodeId + ) + channelDetailViewError = nil + } catch let error as NodeError { + let errorString = handleNodeError(error) + channelDetailViewError = .init( + title: errorString.title, + detail: errorString.detail + ) + } catch { + channelDetailViewError = .init( + title: "Unexpected error", + detail: error.localizedDescription + ) + } }LDKNodeMonday/View Model/Home/Receive/BIP21ViewModel.swift (2)
21-23
: Add documentation for the initializer.Consider adding documentation to explain the parameter requirements and any specific initialization behavior.
+ /// Initializes a new BIP21ViewModel instance. + /// - Parameter lightningClient: The client used for lightning network operations. init(lightningClient: LightningNodeClient) { self.lightningClient = lightningClient }
27-30
: Consider adding input validation for the receive parameters.While the implementation correctly uses the injected client, consider adding validation for the input parameters before making the client call. For example:
- Validate that
amountSat
is greater than 0- Ensure
message
is not empty- Verify
expirySecs
is within a reasonable rangefunc receivePayment(amountSat: UInt64, message: String, expirySecs: UInt32) async { + guard amountSat > 0 else { + DispatchQueue.main.async { + self.receiveViewError = .init( + title: "Invalid amount", + detail: "Amount must be greater than 0" + ) + } + return + } + guard !message.isEmpty else { + DispatchQueue.main.async { + self.receiveViewError = .init( + title: "Invalid message", + detail: "Message cannot be empty" + ) + } + return + } do { let unified = try await lightningClient.receive(Also applies to: 52-52
LDKNodeMonday/View Model/Home/Send/AmountViewModel.swift (3)
18-22
: Great architectural improvement using dependency injection!The introduction of
lightningClient
as an injected dependency improves testability and reduces coupling by moving away from the singleton pattern (LightningNodeService.shared
).
50-54
: Consider Swift naming conventions and access level.While the implementation is correct, consider these improvements:
- Rename
getColor()
toupdateNetworkColor()
to follow Swift naming conventions- Mark the method as
private
since it appears to be used internally- func getColor() { + private func updateNetworkColor() { let color = lightningClient.getNetworkColor() DispatchQueue.main.async { self.networkColor = color } }
Line range hint
13-15
: Consider protocol-oriented design for better testability.Consider extracting the view model's interface into a protocol. This would further improve testability by allowing easy creation of mock implementations for testing.
protocol AmountViewModelProtocol { var networkColor: Color { get } var amountConfirmationViewError: MondayError? { get set } func send(uriStr: String) async throws -> QrPaymentResult func updateNetworkColor() } @MainActor @Observable class AmountViewModel: AmountViewModelProtocol { // ... existing implementation ... }LDKNodeMonday/View/Home/Payments/PaymentsView.swift (1)
55-55
: Consider adding multiple preview scenarios.While using the mock client is good practice, consider adding multiple preview scenarios to test different states:
- Empty payments list
- List with various payment types
- Error states
Example implementation:
#if DEBUG #Preview("Empty State") { PaymentsView(viewModel: .init(lightningClient: .mockEmpty)) } #Preview("With Payments") { PaymentsView(viewModel: .init(lightningClient: .mockWithPayments)) } #Preview("Error State") { PaymentsView(viewModel: .init(lightningClient: .mockWithError)) } #endifLDKNodeMonday/View/Home/BitcoinView.swift (1)
Line range hint
293-336
: Consider extracting repeated refresh logic.The same refresh logic appears multiple times in sheet dismissal handlers. Consider extracting it into a method on the view model:
+ extension BitcoinViewModel { + func refreshAllBalances() async { + await getTotalOnchainBalanceSats() + await getTotalLightningBalanceSats() + await getPrices() + await getSpendableOnchainBalanceSats() + await getStatus() + } + }Then use it in all locations:
- Task { - await viewModel.getTotalOnchainBalanceSats() - await viewModel.getTotalLightningBalanceSats() - await viewModel.getPrices() - await viewModel.getSpendableOnchainBalanceSats() - await viewModel.getStatus() - } + Task { + await viewModel.refreshAllBalances() + }LDKNodeMonday/View Model/Profile/Danger/SeedViewModel.swift (1)
Line range hint
1-36
: Consider adding security-related documentation.Since this view model handles sensitive seed phrase data, consider adding class-level documentation explaining:
- Security considerations
- Proper usage guidelines
- Warning about handling seed phrases
Example addition:
+ /// SeedViewModel: Handles the secure retrieval and display of wallet recovery phrases. + /// - Warning: This class handles sensitive cryptographic material. Ensure all UI interactions + /// are protected and seed phrases are never logged or stored in plain text. class SeedViewModel: ObservableObject {LDKNodeMonday/View/Settings/Danger/SeedView.swift (1)
Line range hint
1-101
: Consider additional security measures for sensitive data display.While the current implementation includes good security practices, consider these enhancements:
- Add blur protection when app enters background
- Add screenshot prevention/warning
- Make clipboard clear duration configurable
Example implementation:
// Add to view modifier chain .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in // Auto-hide seed phrase when app goes to background if showRecoveryPhrase { showRecoveryPhrase = false } } .screenshotProhibited() // iOS 17+ onlyLDKNodeMonday/View Model/Profile/Channel/ChannelsListViewModel.swift (1)
32-41
: Consider improving error handling and type safety.A few suggestions to enhance the implementation:
- Replace
try?
with proper error handling to avoid silently ignoring network fetch errors- Use type-safe network comparison instead of string comparison
- Expand the comment to explain why mainnet-only alias fetching is temporary
Consider this improvement:
- if let networkString = try? keyClient.getNetwork(), - networkString == Network.bitcoin.description + do { + let network = try await keyClient.getNetwork() + // Only fetch aliases on mainnet due to LDK Node limitation + // See: https://github.com/lightningdevkit/ldk-node/issues/234 + guard network == Network.bitcoin else { return } + for channel in channels { + await fetchAlias(for: channel.counterpartyNodeId) + } + } catch { + self.channelsListViewError = .init( + title: "Network Error", + detail: "Failed to determine network: \(error.localizedDescription)" + ) + }LDKNodeMonday/View/Settings/Channel/ChannelsListView.swift (2)
85-91
: Consider extracting magic numbers into constants.The circle dimensions and icon sizes could be defined as constants for better maintainability.
+ private enum Constants { + static let circleSize: CGFloat = 40 + static let iconSize: CGFloat = 16 + } + ZStack { Circle() .stroke(lineWidth: 2) - .frame(width: 40, height: 40) + .frame(width: Constants.circleSize, height: Constants.circleSize) Image(systemName: "fibrechannel") .resizable() .scaledToFit() - .frame(width: 16, height: 16) + .frame(width: Constants.iconSize, height: Constants.iconSize) }
111-114
: Consider improving numerical value formatting.The msat to sat conversion and numerical formatting could be improved:
- Extract the conversion logic to computed properties
- Add proper number formatting for better accessibility
+ private var formattedOutboundSats: String { + let sats = channel.outboundCapacityMsat/1000 + return NumberFormatter.localizedString(from: NSNumber(value: sats), number: .decimal) + } + + private var formattedInboundSats: String { + let sats = channel.inboundCapacityMsat/1000 + return NumberFormatter.localizedString(from: NSNumber(value: sats), number: .decimal) + } + HStack { - Text("Send \(channel.outboundCapacityMsat/1000) sats") + Text("Send \(formattedOutboundSats) sats") Spacer() - Text("Receive \(channel.inboundCapacityMsat/1000) sats") + Text("Receive \(formattedInboundSats) sats") }LDKNodeMonday/View/Settings/Peers/PeersListView.swift (4)
40-42
: Consider adding error handling for peer listing.The
onAppear
handler should handle potential errors when listing peers. Consider showing an error state to improve user experience..onAppear { - viewModel.listPeers() + Task { + do { + await viewModel.listPeers() + } catch { + // Handle error state + } + } }
67-68
: Remove duplicate list style.The
.listStyle(.plain)
modifier is already applied in the parent view, making this redundant.
76-85
: Consider extracting magic numbers into constants.The hard-coded dimensions and spacing values should be extracted into named constants for better maintainability.
+private enum Constants { + static let avatarSize: CGFloat = 40 + static let iconSize: CGFloat = 16 + static let horizontalSpacing: CGFloat = 15 +} -HStack(alignment: .center, spacing: 15) { +HStack(alignment: .center, spacing: Constants.horizontalSpacing) { ZStack { Circle() .stroke(lineWidth: 2) - .frame(width: 40, height: 40) + .frame(width: Constants.avatarSize, height: Constants.avatarSize) Image(systemName: "person.line.dotted.person") .resizable() .scaledToFit() - .frame(width: 16, height: 16) + .frame(width: Constants.iconSize, height: Constants.iconSize) }
98-117
: Reduce code duplication in connection status styling.The font and color modifiers are duplicated between the connected and disconnected states. Consider extracting the common styling.
private struct ConnectionStatus: View { let isConnected: Bool + + private var statusText: String { isConnected ? "Connected" : "Not Connected" } + private var statusIcon: String { isConnected ? "checkmark" : "xmark" } var body: some View { - if isConnected { - HStack(alignment: .firstTextBaseline, spacing: 2) { - Text("Connected") - Image(systemName: "checkmark") - } - .font(.caption) - .foregroundColor(.secondary) - } else { - HStack { - Text("Not Connected") - Image(systemName: "xmark") - } - .font(.caption) - .foregroundColor(.secondary) - } + HStack(alignment: .firstTextBaseline, spacing: 2) { + Text(statusText) + Image(systemName: statusIcon) + } + .font(.caption) + .foregroundColor(.secondary) } }LDKNodeMonday/View Model/Profile/Peer/DisconnectViewModel.swift (1)
16-21
: Add documentation for the initializer parameters.Consider adding a documentation comment to explain the purpose and requirements of each parameter, especially since this is a public initializer.
+ /// Initializes a new DisconnectViewModel + /// - Parameters: + /// - nodeId: The public key of the peer to disconnect + /// - lightningClient: The client used to perform lightning node operations init( nodeId: PublicKey, lightningClient: LightningNodeClient ) {LDKNodeMonday/View/Settings/Peers/PeerView.swift (2)
Line range hint
33-65
: Consider enhancing input validation and feedback.The current implementation could benefit from more robust input validation:
- Add real-time validation feedback for node ID and address formats
- Consider using a dedicated input validation service
- Show validation state in the UI (e.g., green checkmark for valid input)
Example enhancement for the node ID field:
TextField( "03a5b467d7f...4c2b099b8250c", text: $viewModel.nodeId ) +.onChange(of: viewModel.nodeId) { _, newValue in + viewModel.validateNodeId(newValue) +} +.overlay( + HStack { + Spacer() + if viewModel.isValidNodeId { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + } + }.padding(.trailing) +)
Line range hint
142-157
: Consider structuring error handling with an enum.The current error handling uses a custom error type with title and detail. Consider using a structured enum for better type safety and maintainability.
Example implementation:
enum PeerViewError: LocalizedError { case invalidPaste case invalidQRCode case scanError(String) var errorDescription: String { switch self { case .invalidPaste: return "Paste Parsing Error" case .invalidQRCode: return "QR Parsing Error" case .scanError: return "Scan Error" } } var recoverySuggestion: String { switch self { case .invalidPaste: return "Failed to parse the Pasteboard." case .invalidQRCode: return "Failed to parse the QR code." case .scanError(let message): return message } } }LDKNodeMonday/View Model/Profile/Peer/PeersListViewModel.swift (1)
13-13
: LGTM! Good use of dependency injection.The shift from using a shared service to dependency injection via
LightningNodeClient
improves testability and modularity.This architectural change:
- Makes dependencies explicit
- Enables better testing through mock clients
- Reduces coupling with global state
Also applies to: 15-16
LDKNodeMonday/View Model/Profile/Channel/ChannelAddViewModel.swift (1)
43-49
: Document the purpose of the hardcoded boolean parameter.The
false
parameter passed toconnectOpenChannel
lacks context. Consider:
- Adding a named parameter or documentation explaining its purpose
- Using a constant with a descriptive name if this is a configuration value
- false + shouldWaitForScid: false // Add parameter name for clarityLDKNodeMonday/View Model/Home/OnboardingViewModel.swift (1)
27-30
: Consider refactoring repeated save operationsThe calls to
keyClient.saveNetwork
andkeyClient.saveEsploraURL
are repeated in multiple places (selectedNetwork
didSet,selectedEsploraServer
didSet, andsaveSeed()
function). Refactoring these into a separate method could reduce code duplication and improve maintainability.You might extract these repeated save operations into a dedicated method, such as:
private func saveNetworkSettings() throws { try keyClient.saveNetwork(selectedNetwork.description) try keyClient.saveEsploraURL(selectedEsploraServer.url) }Then, update the places where these calls occur:
// In selectedNetwork didSet - try keyClient.saveNetwork(networkString) - try keyClient.saveEsploraURL(selectedEsploraServer.url) + try saveNetworkSettings() // In selectedEsploraServer didSet - try keyClient.saveEsploraURL(selectedEsploraServer.url) + try saveNetworkSettings() // In saveSeed() function - try keyClient.saveNetwork(selectedNetwork.description) - try keyClient.saveEsploraURL(selectedEsploraServer.url) + try saveNetworkSettings()Also applies to: 45-45, 114-116
LDKNodeMonday/App/LDKNodeMondayApp.swift (1)
82-82
: UselightningClient
instead ofLightningNodeClient.live
inapplicationWillTerminate
To maintain consistency with dependency injection and ensure that the same instance of
lightningClient
is used throughout the application lifecycle, consider usinglightningClient.stop()
rather than directly callingLightningNodeClient.live.stop()
.Apply this diff to make the change:
- try? LightningNodeClient.live.stop() + try? lightningClient.stop()This change ensures that if the
lightningClient
has been mocked or altered for testing purposes, the correct instance is stopped upon application termination.LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (1)
31-40
: Consider handling edge cases in USD value calculations.The computed properties should handle potential edge cases:
- Zero balances
- Extremely large numbers that might cause overflow
- Invalid/zero price values
var satsPrice: String { + guard price > 0 else { return "$0.00" } let usdValue = Double(totalBalance).valueInUSD(price: price) return usdValue } var totalUSDValue: String { + guard price > 0 else { return "$0.00" } let totalUSD = Double(totalBalance + totalLightningBalance).valueInUSD(price: price) return totalUSD }LDKNodeMonday/View/Settings/SettingsView.swift (1)
38-45
: Avoid redundant data fetching in both initializer andonAppear
The
viewModel
methodsgetNodeID()
,getNetwork()
,getEsploraUrl()
, andgetStatus()
are called both in the initializer ofSettingsViewModel
and within the.onAppear
modifier inSettingsView
. This results in redundant data fetching and unnecessary network or processing overhead.Consider removing these calls from either the initializer or the
.onAppear
block. If the data is required upon view appearance, it's preferable to keep the calls in.onAppear
and simplify the initializer:- init( - appState: Binding<AppState>, - keyClient: KeyClient = .live, - lightningClient: LightningNodeClient - ) { - _appState = appState - self.keyClient = keyClient - self.lightningClient = lightningClient - // Call these immediately to populate data - getNodeID() - getNetwork() - getEsploraUrl() - Task { - await getStatus() - } - } + init( + appState: Binding<AppState>, + keyClient: KeyClient = .live, + lightningClient: LightningNodeClient + ) { + _appState = appState + self.keyClient = keyClient + self.lightningClient = lightningClient + }LDKNodeMonday/View Model/Profile/SettingsViewModel.swift (2)
34-39
: Avoid redundant data fetching in initializerThe initializer calls
getNodeID()
,getNetwork()
,getEsploraUrl()
, andgetStatus()
. Since these methods are also called in the.onAppear
block ofSettingsView
, this leads to redundant data fetching.Consider removing these calls from the initializer to prevent unnecessary duplicated work:
init( appState: Binding<AppState>, keyClient: KeyClient = .live, lightningClient: LightningNodeClient ) { _appState = appState self.keyClient = keyClient self.lightningClient = lightningClient - // Call these immediately to populate data - getNodeID() - getNetwork() - getEsploraUrl() - Task { - await getStatus() - } }
43-45
: Add error handling togetNodeID()
The
getNodeID()
function does not handle potential errors that may occur when retrieving the node ID. IflightningClient.nodeId()
can fail or throw an error, consider adding error handling to provide user feedback.func getNodeID() { - let nodeID = lightningClient.nodeId() - self.nodeID = nodeID + do { + let nodeID = try lightningClient.nodeId() + self.nodeID = nodeID + } catch { + DispatchQueue.main.async { + self.nodeIDError = .init(title: "Error", detail: error.localizedDescription) + } + } }LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (2)
274-302
: Well-designed client interface!The
LightningNodeClient
struct provides a clean, injectable interface that enhances testability. The closure-based design effectively encapsulates theLightningNodeService
functionality.Consider adding documentation comments to describe the purpose of the struct and its role in the architecture. This would help other developers understand the motivation behind this abstraction.
+/// A client interface for interacting with the Lightning Node Service. +/// This struct provides a testable abstraction layer over `LightningNodeService`, +/// enabling dependency injection and easier mocking in tests. struct LightningNodeClient {
374-422
: Enhance mock implementation for better testing.The mock implementation provides good test data, but consider these improvements:
- Make mock values configurable to support different test scenarios
- Use more realistic test data for node ID and addresses
Example of making values configurable:
extension LightningNodeClient { + struct MockConfig { + var nodeId: String + var spendableBalance: UInt64 + // ... other configurable values + + static let standard = MockConfig( + nodeId: "038474837483784378437843784378437843784378", + spendableBalance: 100_000 + ) + } + static let mock = Self( start: {}, stop: {}, - nodeId: { "038474837483784378437843784378437843784378" }, + nodeId: { MockConfig.standard.nodeId }, // ... configure other values similarly ) + + static func mock(config: MockConfig) -> Self { + Self( + start: {}, + stop: {}, + nodeId: { config.nodeId }, + // ... use config values + ) + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (31)
LDKNodeMonday/App/LDKNodeMondayApp.swift
(4 hunks)LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift
(1 hunks)LDKNodeMonday/View Model/Home/BitcoinViewModel.swift
(2 hunks)LDKNodeMonday/View Model/Home/OnboardingViewModel.swift
(6 hunks)LDKNodeMonday/View Model/Home/Payments/PaymentsViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Home/Receive/BIP21ViewModel.swift
(2 hunks)LDKNodeMonday/View Model/Home/Receive/JITInvoiceViewModel.swift
(2 hunks)LDKNodeMonday/View Model/Home/Send/AmountViewModel.swift
(2 hunks)LDKNodeMonday/View Model/Profile/Channel/ChannelAddViewModel.swift
(3 hunks)LDKNodeMonday/View Model/Profile/Channel/ChannelDetailViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/Channel/ChannelsListViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/Danger/SeedViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/Peer/DisconnectViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/Peer/PeerViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/Peer/PeersListViewModel.swift
(1 hunks)LDKNodeMonday/View Model/Profile/SettingsViewModel.swift
(3 hunks)LDKNodeMonday/View/Home/BitcoinView.swift
(5 hunks)LDKNodeMonday/View/Home/OnboardingView.swift
(1 hunks)LDKNodeMonday/View/Home/Payments/PaymentsView.swift
(1 hunks)LDKNodeMonday/View/Home/Receive/BIP21View.swift
(1 hunks)LDKNodeMonday/View/Home/Receive/JITInvoiceView.swift
(1 hunks)LDKNodeMonday/View/Home/Receive/ReceiveView.swift
(2 hunks)LDKNodeMonday/View/Home/Send/AmountView.swift
(1 hunks)LDKNodeMonday/View/Settings/Channel/ChannelAddView.swift
(1 hunks)LDKNodeMonday/View/Settings/Channel/ChannelDetailView.swift
(1 hunks)LDKNodeMonday/View/Settings/Channel/ChannelsListView.swift
(3 hunks)LDKNodeMonday/View/Settings/Danger/SeedView.swift
(1 hunks)LDKNodeMonday/View/Settings/Peers/PeerDetailsView.swift
(1 hunks)LDKNodeMonday/View/Settings/Peers/PeerView.swift
(1 hunks)LDKNodeMonday/View/Settings/Peers/PeersListView.swift
(1 hunks)LDKNodeMonday/View/Settings/SettingsView.swift
(1 hunks)
✅ Files skipped from review due to trivial changes (4)
- LDKNodeMonday/View/Settings/Channel/ChannelAddView.swift
- LDKNodeMonday/View/Home/Receive/BIP21View.swift
- LDKNodeMonday/View/Home/Send/AmountView.swift
- LDKNodeMonday/View/Home/Receive/JITInvoiceView.swift
🔇 Additional comments (53)
LDKNodeMonday/View/Home/Receive/ReceiveView.swift (3)
12-12
: LGTM! Good use of dependency injection.The immutable
lightningClient
property ensures proper initialization and dependency management.
35-35
: LGTM! Proper dependency injection in view model initialization.The
lightningClient
is correctly passed to the JITInvoiceView's view model.
37-37
: LGTM! Consistent dependency injection pattern.The
lightningClient
is correctly passed to the BIP21View's view model, maintaining consistency with JITInvoiceView.LDKNodeMonday/View Model/Home/Receive/JITInvoiceViewModel.swift (2)
19-23
: Great architectural improvement!The switch from singleton pattern (
LightningNodeService.shared
) to dependency injection improves testability and follows SOLID principles. The private access level forlightningClient
properly encapsulates the dependency.
32-36
: Clean implementation of the client usage!The update maintains proper error handling while cleanly integrating with the new
lightningClient
. The async/await pattern and main thread dispatching are handled correctly.LDKNodeMonday/View Model/Profile/Channel/ChannelDetailViewModel.swift (2)
14-14
: LGTM! Well-structured property declaration.The property is correctly declared as private and immutable, following Swift best practices for dependency injection.
16-21
: LGTM! Clean initializer implementation.The initializer follows a good pattern for dependency injection and maintains clean formatting with parameters on separate lines.
LDKNodeMonday/View Model/Home/Receive/BIP21ViewModel.swift (1)
19-19
: LGTM! Good use of immutability and encapsulation.The property is correctly declared as private and immutable, ensuring thread safety and proper encapsulation.
LDKNodeMonday/View Model/Home/Send/AmountViewModel.swift (1)
26-26
: LGTM! Consistent with the new architecture.The change properly utilizes the injected client while maintaining robust error handling.
LDKNodeMonday/View Model/Home/Payments/PaymentsViewModel.swift (3)
14-14
: LGTM! Good encapsulation.The private property ensures proper encapsulation of the lightning client dependency.
16-18
: LGTM! Good use of dependency injection.The initializer properly implements dependency injection, making the code more testable and flexible.
20-22
: LGTM! Clean implementation.The listPayments method cleanly delegates to the injected client, maintaining single responsibility.
LDKNodeMonday/View/Home/BitcoinView.swift (5)
242-247
: LGTM! Clean dependency injection implementation.The addition of
lightningClient
parameter follows good dependency injection practices.
293-293
: LGTM! Proper dependency passing to sheet view.The
lightningClient
is correctly passed to the presented sheet view.
308-308
: LGTM! Consistent dependency injection pattern.The
lightningClient
is properly injected into the PaymentsView's viewModel.
Line range hint
322-328
: LGTM! Good data refresh implementation.The
lightningClient
is properly injected, and the balance refresh on view disappear ensures data consistency.
355-359
: LGTM! Proper preview setup with mock client.Using
.mock
forlightningClient
in preview follows best practices for SwiftUI previews.LDKNodeMonday/View Model/Profile/Danger/SeedViewModel.swift (1)
15-19
: Great job implementing dependency injection!Moving away from the singleton pattern to dependency injection improves testability and flexibility.
LDKNodeMonday/View/Settings/Danger/SeedView.swift (1)
101-101
: LGTM! Preview updated correctly.Preview now uses the mock client, maintaining consistency with the ViewModel changes.
LDKNodeMonday/View Model/Profile/Channel/ChannelsListViewModel.swift (2)
15-16
: LGTM! Well-structured property declarations.The new properties follow Swift best practices with appropriate access levels and immutability.
21-28
: LGTM! Clean dependency injection implementation.The initializer is well-structured with:
- Clear parameter ordering
- Sensible default value for
keyClient
- Proper property initialization
LDKNodeMonday/View/Settings/Channel/ChannelsListView.swift (6)
9-9
: LGTM! Import statement is appropriate.The addition of the
LDKNode
import aligns with the integration of Lightning Node functionality.
31-34
: LGTM! Proper dependency injection.The
lightningClient
is correctly passed to the detail view through the view model.
38-41
: LGTM! Good modularization.Extracting the channel presentation logic into a separate
ChannelRow
view improves code organization and reusability.
58-62
: LGTM! Enhanced accessibility.Good addition of accessibility support with dynamic type sizing and proper navigation styling.
63-74
: LGTM! Well-structured navigation.The toolbar implementation follows SwiftUI best practices and properly passes the
lightningClient
to the add view.
124-124
: LGTM! Proper preview setup.The preview correctly initializes the view with mock clients for both node info and lightning functionality.
LDKNodeMonday/View/Settings/Peers/PeersListView.swift (3)
20-21
: LGTM! Clean separation of concerns.Good job extracting the peer list logic into a separate component, making the code more maintainable.
23-26
: Great accessibility and UI configuration.Excellent attention to accessibility with dynamic type size support and proper list styling.
46-49
: Good use of dependency injection.Clean implementation with explicit dependencies through parameters rather than global state.
LDKNodeMonday/View/Settings/Channel/ChannelDetailView.swift (1)
159-160
: LGTM! Good use of dependency injection with mock client in preview.The addition of
lightningClient: .mock
to the preview's view model initialization follows good testing practices by using dependency injection.However, the AI summary mentions changes to the
ChannelDetailViewModel
class that are not visible in the provided code. Let's verify these changes:✅ Verification successful
Verified: Dependency injection and mock client usage are properly implemented
The preview's use of
.mock
client aligns perfectly with the view model's implementation:
- ChannelDetailViewModel properly accepts lightningClient through dependency injection
- The close() method correctly utilizes the injected client
- Error handling and threading considerations are well implemented
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for ChannelDetailViewModel implementation rg -A 10 "class ChannelDetailViewModel" # Verify the view model's usage of lightningClient ast-grep --pattern 'class ChannelDetailViewModel { $$$ func close() { $$$ } $$$ }'Length of output: 1284
Script:
#!/bin/bash # Search for close method in ChannelDetailViewModel rg -A 10 "func close\(\)" "LDKNodeMonday/View Model/Profile/Channel/ChannelDetailViewModel.swift"Length of output: 510
LDKNodeMonday/View Model/Profile/Peer/DisconnectViewModel.swift (2)
14-14
: LGTM! Well-structured property declaration.The private access level is appropriate for this internal dependency, following good encapsulation principles.
26-26
: Verify error handling coverage for LightningNodeClient.The error handling looks comprehensive, but please verify that all possible error cases from
LightningNodeClient.disconnect()
are properly handled.✅ Verification successful
Error handling coverage is appropriate
The implementation properly handles both specific
NodeError
cases and unexpected errors through the catch-all block. The error propagation chain from LDK through the service layer to the view model is correctly maintained.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all error types that LightningNodeClient.disconnect might throw ast-grep --pattern 'func disconnect($$$) throws $error { $$$ }'Length of output: 67
Script:
#!/bin/bash # Find files containing LightningNodeClient echo "=== Files containing LightningNodeClient ===" rg "LightningNodeClient" -l echo -e "\n=== LightningNodeClient implementation ===" rg "class LightningNodeClient|protocol LightningNodeClient" -A 10 echo -e "\n=== Searching for disconnect method ===" rg "func disconnect.*\(" -A 5Length of output: 2672
LDKNodeMonday/View/Home/OnboardingView.swift (1)
105-105
: LGTM! Good use of dependency injection.The preview modification correctly implements the new dependency injection pattern, improving testability by using the mock client.
LDKNodeMonday/View/Settings/Peers/PeerView.swift (2)
211-211
: LGTM! Good use of dependency injection in preview.The change to use a mock client in the preview is a good practice, making the preview more reliable and independent of the actual Lightning node service.
211-211
: Verify view model implementation for proper dependency injection.The preview changes suggest that
PeerViewModel
has been updated to use dependency injection withLightningNodeClient
. Let's verify the implementation:✅ Verification successful
✅ Dependency injection properly implemented
The
PeerViewModel
correctly implements dependency injection:
- Declares
lightningClient
as a required dependency- Uses the injected client throughout its implementation
- Preview correctly uses mock client
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for PeerViewModel implementation rg -l "class PeerViewModel" # Check if connect method uses lightningClient instead of shared service ast-grep --pattern 'class PeerViewModel { $$$ func connect($$$) { $$$ } $$$ }'Length of output: 180
Script:
#!/bin/bash # Check class initialization and stored property ast-grep --pattern 'class PeerViewModel { let lightningClient: $_ $$$ }' # Check for any remaining usage of shared instance rg -A 3 "LightningNodeClient\.shared" "LDKNodeMonday/View Model/Profile/Peer/PeerViewModel.swift" # View the full implementation for thorough analysis cat "LDKNodeMonday/View Model/Profile/Peer/PeerViewModel.swift"Length of output: 1578
LDKNodeMonday/View Model/Profile/Peer/PeersListViewModel.swift (1)
19-21
: Verify error handling for Lightning Network operations.The
listPeers()
implementation appears to assume the operation always succeeds. Lightning Network operations can fail due to various reasons (node offline, network issues, etc.).Let's check if error handling exists in the client implementation:
Consider adding error handling if not already present in the client layer:
func listPeers() { do { self.peers = try lightningClient.listPeers() } catch { // Handle error appropriately self.peers = [] // Consider adding error state handling } }LDKNodeMonday/View Model/Profile/Channel/ChannelAddViewModel.swift (2)
18-19
: LGTM! Good use of dependency injection.The addition of the private
lightningClient
property improves testability and follows good architectural principles by removing direct dependency on shared services.
29-31
: Verify all instantiations of ChannelAddViewModel.The initializer change requires all existing instantiations of
ChannelAddViewModel
to be updated with aLightningNodeClient
instance.✅ Verification successful
All ChannelAddViewModel instantiations are properly updated
The initializer changes have been correctly implemented across the codebase:
- Production usage in ChannelsListView properly initializes with
lightningClient
- Preview code correctly uses the mock client
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for ChannelAddViewModel instantiations to ensure they've been updated rg -l "ChannelAddViewModel\(" | xargs rg "ChannelAddViewModel\([^)]*\)"Length of output: 73
Script:
#!/bin/bash # First, find all files containing ChannelAddViewModel echo "=== Files containing ChannelAddViewModel ===" rg --type swift "ChannelAddViewModel" -l echo -e "\n=== Usages of ChannelAddViewModel with context ===" rg --type swift -B 2 -A 2 "ChannelAddViewModel"Length of output: 1796
Script:
#!/bin/bash # Get the full content of ChannelAddView.swift to find initialization echo "=== Full content of ChannelAddView.swift ===" cat "LDKNodeMonday/View/Settings/Channel/ChannelAddView.swift"Length of output: 9371
Script:
#!/bin/bash # Search for files that create ChannelAddView echo "=== Files containing ChannelAddView creation ===" rg -B 3 -A 3 "ChannelAddView\(" --type swiftLength of output: 1303
LDKNodeMonday/View Model/Home/OnboardingViewModel.swift (4)
13-14
: Implementing dependency injection forlightningClient
andkeyClient
Introducing
lightningClient
andkeyClient
as private properties and injecting them via the initializer enhances modularity and testability by avoiding reliance on singleton instances.
82-89
: Updating initializer for dependency injectionThe updated initializer now accepts
lightningClient
andkeyClient
as parameters, promoting dependency injection and enhancing testability by allowing the injection of mock clients during testing.
92-95
: Proper error handling when retrieving stored settingsUsing
keyClient.getNetwork()
andkeyClient.getEsploraURL()
with proper error handling ensures that the application gracefully handles missing or corrupted stored data.
139-139
: Consistent use oflightningClient
to retrieve network colorUsing
lightningClient.getNetworkColor()
maintains consistency with the dependency injection approach and removes reliance on singleton patterns.LDKNodeMonday/View Model/Profile/Peer/PeerViewModel.swift (2)
17-22
: Implementing dependency injection forlightningClient
Adding
lightningClient
as a private property and injecting it via the initializer enhances modularity and testability by avoiding dependency on a singleton instance.
28-31
: UsinglightningClient
in theconnect
methodSwitching to
lightningClient.connect()
aligns with the dependency injection pattern and ensures that thePeerViewModel
is decoupled from global instances, improving testability.LDKNodeMonday/App/LDKNodeMondayApp.swift (4)
15-15
: InitializinglightningClient
as a private propertyIntroducing
lightningClient
as a private property allows for centralized management of the Lightning Node Client within the app and facilitates dependency injection into view models.
25-27
: PassinglightningClient
toOnboardingViewModel
Injecting
lightningClient
intoOnboardingViewModel
promotes dependency injection, enhancing modularity and allowing for easier testing and maintenance.
30-34
: PassinglightningClient
toBitcoinViewModel
Injecting
lightningClient
intoBitcoinViewModel
ensures consistency in how dependencies are provided throughout the app, improving testability and modularity.
60-61
: UsinglightningClient
to start and listen for eventsReplacing
LightningNodeService.shared
withlightningClient
in thestart()
function adheres to the dependency injection pattern and eliminates reliance on singleton instances.LDKNodeMonday/View Model/Home/BitcoinViewModel.swift (2)
26-30
: Clean dependency injection implementation!The
lightningClient
property properly introduces dependency injection, improving testability and modularity.
Line range hint
52-111
: Methods correctly updated to use lightningClient.All service method calls have been properly migrated to use the injected
lightningClient
, maintaining the existing asynchronous patterns and error handling.LDKNodeMonday/View/Settings/SettingsView.swift (1)
22-58
: RefactoredSettingsView
improves modularity and readabilityThe refactoring of
SettingsView
to utilizeSettingsFormView
and dedicated subsections (WalletSection
,LightningNodeSection
,DangerZoneSection
) enhances the modularity and maintainability of the codebase. Breaking down the settings into smaller, reusable components improves readability and promotes better organization.LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift (1)
329-338
: Parameter mismatch fixed.The
announceChannel
parameter is now correctly passed to the service method.
There was a problem hiding this 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
🧹 Nitpick comments (2)
LDKNodeMonday/View Model/Profile/SettingsViewModel.swift (1)
21-39
: Consider deferring async initializationWhile dependency injection of
lightningClient
is good for testability, calling async methods directly ininit
could lead to race conditions. Consider moving the initialization logic to a separate async setup method.init( appState: Binding<AppState>, keyClient: KeyClient = .live, lightningClient: LightningNodeClient ) { _appState = appState self.keyClient = keyClient self.lightningClient = lightningClient - // Call these immediately to populate data, wasnt immediately doing it otherwise? - getNodeID() - getNetwork() - getEsploraUrl() - Task { - await getStatus() - } } + +func setup() async { + getNodeID() + getNetwork() + getEsploraUrl() + await getStatus() +}LDKNodeMonday/View/Settings/SettingsView.swift (1)
148-177
: Consider enhancing copy feedback UXThe current implementation has a hard-coded 2-second delay for the checkmark. Consider:
- Using haptic feedback for better user experience
- Making the duration configurable
Button { UIPasteboard.general.string = viewModel.nodeID showCheckmark = true + UINotificationFeedbackGenerator().notificationOccurred(.success) DispatchQueue.main.asyncAfter(deadline: .now() + 2) { showCheckmark = false } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
LDKNodeMonday/View Model/Profile/SettingsViewModel.swift
(3 hunks)LDKNodeMonday/View/Settings/SettingsView.swift
(1 hunks)
🔇 Additional comments (5)
LDKNodeMonday/View Model/Profile/SettingsViewModel.swift (3)
Line range hint
43-61
: LGTM! Good error handling patternThe error handling pattern is well-implemented, properly distinguishing between
NodeError
and other error types.
Line range hint
66-91
: Improve error handling in delete methodThe error handling could be improved by:
- Making the method async to properly handle asynchronous operations
- Adding specific error handling for
deleteNetwork
anddeleteEsplora
operationsThis issue was previously identified in a past review. Please refer to the existing suggestion for the implementation details.
128-134
: Verify async requirement for getStatusThe method is marked as async but
lightningClient.status()
appears to be synchronous. Ifstatus()
is indeed synchronous, this method doesn't need to be async.This issue was previously identified in a past review. To verify the async requirement, let's check the implementation:
#!/bin/bash # Search for the status method implementation in LightningNodeClient ast-grep --pattern 'func status() { $$$'LDKNodeMonday/View/Settings/SettingsView.swift (2)
22-58
: LGTM! Good view modularizationBreaking down the view into smaller components improves maintainability and readability.
224-224
: LGTM! Good preview setupUsing the mock client for previews is a good practice for SwiftUI development.
private struct DangerZoneSection: View { | ||
@ObservedObject var viewModel: SettingsViewModel | ||
@Binding var showStopNodeConfirmation: Bool | ||
@Binding var showDeleteSeedConfirmation: Bool | ||
let dismiss: DismissAction | ||
|
||
var body: some View { | ||
Section { | ||
Button { | ||
showStopNodeConfirmation = true | ||
} label: { | ||
Text("Stop Node") | ||
} | ||
.foregroundColor(.red) | ||
.alert("Warning!", isPresented: $showStopNodeConfirmation) { | ||
Button("Yes", role: .destructive) { viewModel.stop() } | ||
Button("No", role: .cancel) {} | ||
} message: { | ||
Text("Are you sure you want to stop the node?") | ||
} | ||
|
||
Button { | ||
showDeleteSeedConfirmation = true | ||
} label: { | ||
Text("Delete Wallet") | ||
} | ||
.foregroundColor(.red) | ||
.alert("Warning!", isPresented: $showDeleteSeedConfirmation) { | ||
Button("Yes", role: .destructive) { | ||
viewModel.delete() | ||
dismiss() | ||
} | ||
Button("No", role: .cancel) {} | ||
} message: { | ||
Text("All funds will be lost.\nAre you sure you want to delete the wallet?") | ||
} | ||
} header: { | ||
Text("Danger Zone").foregroundColor(.primary) | ||
} | ||
.foregroundColor(.primary) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add loading states to prevent multiple operations
The dangerous operations (stop node, delete wallet) should disable buttons during execution to prevent multiple triggers.
struct DangerZoneSection: View {
@ObservedObject var viewModel: SettingsViewModel
@Binding var showStopNodeConfirmation: Bool
@Binding var showDeleteSeedConfirmation: Bool
+ @State private var isProcessing = false
let dismiss: DismissAction
var body: some View {
Section {
Button {
showStopNodeConfirmation = true
} label: {
Text("Stop Node")
}
.foregroundColor(.red)
+ .disabled(isProcessing)
.alert("Warning!", isPresented: $showStopNodeConfirmation) {
- Button("Yes", role: .destructive) { viewModel.stop() }
+ Button("Yes", role: .destructive) {
+ isProcessing = true
+ viewModel.stop()
+ isProcessing = false
+ }
Button("No", role: .cancel) {}
}
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this 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
🔭 Outside diff range comments (1)
LDKNodeMonday.xcodeproj/project.pbxproj (1)
Line range hint
1037-1041
: Remove duplicate LDKNode package dependencies.There are multiple entries for the LDKNode package dependency. This could lead to confusion and potential versioning conflicts.
Keep only the first entry with the exact version requirement and remove the duplicate entries:
- AE060C372C051B59006724F1 /* LDKNode */ = { - isa = XCSwiftPackageProductDependency; - productName = LDKNode; - }; - AE80C2042C4AB5E4006E7193 /* LDKNode */ = { - isa = XCSwiftPackageProductDependency; - productName = LDKNode; - };Also applies to: 1042-1046, 1047-1051
🧹 Nitpick comments (4)
LDKNodeMonday/View Model/Profile/SettingsViewModel.swift (4)
33-39
: Consider deferring immediate data population.The initializer makes multiple synchronous and one asynchronous call immediately, with a comment suggesting data population issues. This could lead to performance impacts during view model creation.
Consider introducing a separate
setup()
method:init( appState: Binding<AppState>, keyClient: KeyClient = .live, lightningClient: LightningNodeClient ) { _appState = appState self.keyClient = keyClient self.lightningClient = lightningClient - // Call these immediately to populate data, wasnt immediately doing it otherwise? - getNodeID() - getNetwork() - getEsploraUrl() - Task { - await getStatus() - } + setup() } + + private func setup() { + Task { + // Populate initial data + getNodeID() + getNetwork() + getEsploraUrl() + await getStatus() + } + }
21-21
: Add documentation for the lightning client property.Add a documentation comment explaining the purpose and lifecycle of the
lightningClient
property.+ /// Client for interacting with the Lightning node. This instance is shared across the app + /// to maintain a single connection to the node. let lightningClient: LightningNodeClient
Line range hint
94-118
: Reduce error handling duplication.The error handling pattern is duplicated across multiple methods. Consider extracting it into a reusable function.
+ private func handleError(_ operation: () throws -> Void) { + do { + try operation() + } catch let error as NodeError { + let errorString = handleNodeError(error) + DispatchQueue.main.async { + self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) + } + } catch { + DispatchQueue.main.async { + self.nodeIDError = .init( + title: "Unexpected error", + detail: error.localizedDescription + ) + } + } + } func getNetwork() { - do { + handleError { let network = try keyClient.getNetwork() DispatchQueue.main.async { self.network = network } - } catch let error as NodeError { - let errorString = handleNodeError(error) - DispatchQueue.main.async { - self.nodeIDError = .init(title: errorString.title, detail: errorString.detail) - } - } catch { - DispatchQueue.main.async { - self.nodeIDError = .init( - title: "Unexpected error", - detail: error.localizedDescription - ) - } } }
134-141
: Remove debug print statements.Debug print statements should be removed before merging to production.
func getStatus() async { let status = lightningClient.status() await MainActor.run { - print("SettingsViewModel - Setting status on main thread") self.status = status self.isStatusFinished = true - print("SettingsViewModel - Status set: \(String(describing: self.status))") } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
LDKNodeMonday.xcodeproj/project.pbxproj
(2 hunks)LDKNodeMonday.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
(2 hunks)LDKNodeMonday/View Model/Profile/SettingsViewModel.swift
(5 hunks)
🔇 Additional comments (6)
LDKNodeMonday/View Model/Profile/SettingsViewModel.swift (1)
134-141
: Ensure correct asynchronous handling ingetStatus()
The function is marked as
async
but doesn't contain any awaitable operations except for theMainActor.run
.LDKNodeMonday.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (3)
18-19
: Verify compatibility with CodeScanner 2.5.2.The significant version jump (2.0.0 -> 2.5.2) might introduce breaking changes. Please verify that the QR code scanning functionality still works as expected.
✅ Verification successful
CodeScanner update to 2.5.2 appears safe
The codebase uses only basic QR scanning functionality that remains unchanged in version 2.x. All usage patterns are compatible with the updated version.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for CodeScanner usage to identify potential compatibility issues rg -l "CodeScanner" | while read -r file; do if [[ "$file" =~ \.swift$ ]]; then echo "=== $file ===" rg "import.*CodeScanner|CodeScanner" "$file" -A 5 fi doneLength of output: 3361
45-46
: Verify SimpleToast integration after version bump.The update from 0.8.1 to 0.10.1 might introduce breaking changes in the toast notification system. Please verify that all toast notifications still work as expected.
✅ Verification successful
SimpleToast integration verified successfully after version bump
The current implementation uses stable SimpleToast APIs (basic presentation binding and standard configuration options) that are compatible across versions 0.8.1 and 0.10.1. No breaking changes impact detected.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for SimpleToast usage echo "=== Checking Toast Usage ===" rg "import.*SimpleToast|\.toast" -A 3Length of output: 336
Script:
#!/bin/bash # Search for toast configurations and usage in BitcoinView echo "=== Checking Toast Configuration and Usage ===" rg "\.toast|ToastStyle|showToast" BitcoinView.swift -A 5 echo -e "\n=== Checking View Implementation ===" ast-grep --pattern 'struct BitcoinView: View { $$$ var body: some View { $$$ } $$$ }'Length of output: 408
Script:
#!/bin/bash # Find the correct path to BitcoinView.swift echo "=== Finding BitcoinView.swift ===" fd BitcoinView.swift echo -e "\n=== Checking Toast Configuration and Usage ===" fd BitcoinView.swift --exec rg "\.toast|ToastStyle|showToast" {} -A 5 echo -e "\n=== Checking View Implementation ===" fd BitcoinView.swift --exec ast-grep --pattern 'struct BitcoinView: View { $$$ var body: some View { $$$ } $$$ }' {}Length of output: 1274
36-37
: Verify LDK Node compatibility with new client implementation.The update to ldk-node 0.4.2 aligns with the introduction of
LightningNodeClient
. Please ensure:
- All new LDK Node APIs are properly integrated into the client
- No deprecated APIs are being used
✅ Verification successful
LDK Node 0.4.2 integration verified successfully
The codebase correctly implements all new LDK Node APIs and shows no usage of deprecated functionality. Key verifications:
- Proper implementation of unified QR payments and JIT channels
- Modern event handling pattern
- Current balance and payment APIs
- Up-to-date channel and peer management
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for LDK Node API usage echo "=== Checking LDK Node API Usage ===" rg "import.*ldk_node|LDKNode" -A 5 # Search for potential deprecated API calls echo "=== Checking Lightning Service Implementation ===" fd -e swift | xargs rg "LightningNodeService|LightningNodeClient" -A 10Length of output: 67323
Script:
#!/bin/bash # Search for LightningNodeService implementation echo "=== Checking LightningNodeService Implementation ===" ast-grep --pattern 'class LightningNodeService { $$$ }' # Search for LightningNodeClient implementation echo "=== Checking LightningNodeClient Implementation ===" ast-grep --pattern 'protocol LightningNodeClient { $$$ }' # Search for specific API usage patterns echo "=== Checking API Usage Patterns ===" rg "Node\..*\(|\.connect\(|\.send_payment\(|\.receive_payment\(" -A 2Length of output: 7897
LDKNodeMonday.xcodeproj/project.pbxproj (2)
825-825
: LGTM: Development team configuration added.The development team identifier has been properly configured for both Debug and Release configurations, which is necessary for code signing and deploying to devices.
Also applies to: 858-858
Line range hint
1012-1016
: Consider updating dependency version constraints.While using exact versions ensures reproducible builds, consider:
- Regularly checking for security updates
- Setting minimum version constraints instead of exact versions for non-critical dependencies
Run this script to check for newer versions and security advisories:
Also applies to: 1017-1021, 1022-1026, 1027-1031, 1032-1036
✅ Verification successful
Dependencies are up-to-date and well-maintained
While exact version constraints are used, all dependencies are currently at their latest versions and no security vulnerabilities were found. The consistent version management suggests active maintenance, which mitigates the primary concerns about using exact versions.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for newer versions and security advisories for project dependencies # Check latest versions on GitHub echo "Checking latest versions..." gh api graphql -f query=' { repository(owner: "lightningdevkit", name: "ldk-node") { releases(last: 1) { nodes { tagName } } } repository(owner: "kishikawakatsumi", name: "KeychainAccess") { releases(last: 1) { nodes { tagName } } } repository(owner: "sanzaru", name: "SimpleToast") { releases(last: 1) { nodes { tagName } } } repository(owner: "twostraws", name: "CodeScanner") { releases(last: 1) { nodes { tagName } } } }' # Check for security advisories echo "Checking security advisories..." gh api graphql -f query=' { securityVulnerabilities(first: 10, ecosystem: SWIFT, package: "KeychainAccess") { nodes { advisory { summary severity publishedAt } vulnerableVersionRange } } }'Length of output: 1925
Script:
#!/bin/bash # First get current versions from project file echo "Current versions in project.pbxproj:" rg "path = .*\.git.*tag = (.*?)," -o --replace '$1' project.pbxproj # Check repositories individually echo -e "\nChecking ldk-node latest release:" gh api repos/lightningdevkit/ldk-node/releases/latest --jq .tag_name echo -e "\nChecking KeychainAccess latest release:" gh api repos/kishikawakatsumi/KeychainAccess/releases/latest --jq .tag_name echo -e "\nChecking SimpleToast latest release:" gh api repos/sanzaru/SimpleToast/releases/latest --jq .tag_name echo -e "\nChecking CodeScanner latest release:" gh api repos/twostraws/CodeScanner/releases/latest --jq .tag_name # Check security advisories for all packages for pkg in "KeychainAccess" "SimpleToast" "CodeScanner" do echo -e "\nChecking security advisories for $pkg:" gh api graphql -f query=" { securityVulnerabilities(first: 10, ecosystem: SWIFT, package: \"$pkg\") { nodes { advisory { summary severity publishedAt } vulnerableVersionRange } } }" --jq '.data.securityVulnerabilities.nodes[]' doneLength of output: 2314
Script:
#!/bin/bash echo "Current versions in project.pbxproj:" rg "path = .*\.git.*tag = (.*?)," -o --replace '$1' LDKNodeMonday.xcodeproj/project.pbxprojLength of output: 176
Script:
#!/bin/bash echo "Searching for dependency versions in project.pbxproj:" rg "tag|version" -A 2 -B 2 LDKNodeMonday.xcodeproj/project.pbxprojLength of output: 578
if lightningClient.status().isRunning { | ||
try lightningClient.stop() | ||
} | ||
try LightningNodeService.shared.deleteDocuments() | ||
try LightningNodeService.shared.deleteWallet() | ||
try lightningClient.deleteDocuments() | ||
try lightningClient.deleteWallet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Address potential race condition in delete operation.
The status check and stop operation could race if the node status changes between the check and the stop call.
Consider handling the stop operation more safely:
- if lightningClient.status().isRunning {
- try lightningClient.stop()
- }
+ // Attempt to stop regardless of status, handle any errors
+ try? lightningClient.stop()
try lightningClient.deleteDocuments()
try lightningClient.deleteWallet()
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if lightningClient.status().isRunning { | |
try lightningClient.stop() | |
} | |
try LightningNodeService.shared.deleteDocuments() | |
try LightningNodeService.shared.deleteWallet() | |
try lightningClient.deleteDocuments() | |
try lightningClient.deleteWallet() | |
// Attempt to stop regardless of status, handle any errors | |
try? lightningClient.stop() | |
try lightningClient.deleteDocuments() | |
try lightningClient.deleteWallet() |
There was a problem hiding this 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
🧹 Nitpick comments (1)
LDKNodeMonday/App/LDKNodeMondayApp.swift (1)
84-88
: Improve error handling in applicationWillTerminate.While using
try?
silently ignores errors, it might be better to log any failures during node shutdown for debugging purposes.Consider this approach:
func applicationWillTerminate(_ application: UIApplication) { - try? lightningClient?.stop() + do { + try lightningClient?.stop() + } catch { + print("Failed to stop lightning node: \(error)") + // Consider using a proper logging framework + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
LDKNodeMonday/App/LDKNodeMondayApp.swift
(3 hunks)LDKNodeMonday/View Model/Profile/SettingsViewModel.swift
(5 hunks)LDKNodeMonday/View/Home/Send/AddressView.swift
(0 hunks)
💤 Files with no reviewable changes (1)
- LDKNodeMonday/View/Home/Send/AddressView.swift
🚧 Files skipped from review as they are similar to previous changes (1)
- LDKNodeMonday/View Model/Profile/SettingsViewModel.swift
🔇 Additional comments (3)
LDKNodeMonday/App/LDKNodeMondayApp.swift (3)
29-38
: Well-structured dependency injection!The view models are properly initialized with the
lightningClient
instance, following SwiftUI best practices for dependency injection. This ensures consistent state management across the view hierarchy.
64-65
: Consider enhancing error handling and addressing the TODO.While the migration to
lightningClient
is good, there are two areas for improvement:
- The TODO comment indicates that
.start
could accept configuration parameters frombackupInfo
. This would improve flexibility.- The error handling could be more specific to handle different failure scenarios.
Would you like me to:
- Help implement the parameter passing from
backupInfo
to.start
?- Generate more specific error handling cases?
15-15
: Consider thread safety implications of shared state.While initializing a single
lightningClient
instance aligns with the PR objectives, accessing it throughAppDelegate.shared
could lead to thread safety issues if the client is accessed concurrently from multiple threads.Consider adding synchronization mechanisms or documenting thread safety requirements. Let me verify the usage pattern:
Also applies to: 20-22
Description
Creates a LightningNodeClient similar to how BDKClient wraps BDKService to provide a clean interface for all the service methods.
lightningClient
manages a stateful connection to the Lightning node. We create one instance at the app level and pass it down through the view hierarchy.This ensures:
That's why in LDKNodeMondayApp.swift we create a single instance of
.live
and pass this same instance down through the views, rather than creating new .live instances everywhere.This is a common pattern for services that maintain state or connections - you want a single source of truth rather than multiple independent instances.
Notes to the reviewers
In some of the views I had to refactor breaking things up because I kept getting
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
when trying to build, so that's the reason I had to do that (to build!).Cleaned up some spots that were calling KeyService directly and shouldn't have been.
I just set the
DEVELOPMENT_TEAM
back to my ID for the moment, if that makes things way more annoying for you lmk.Testing
Changelog notice
Checklists
All Submissions:
.swift-format
fileNew Features:
Bugfixes:
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Bug Fixes
Refactor