Skip to content

Conversation

@ben-kaufman
Copy link
Contributor

This PR is based on the RN migration branch, and adds support for restoring an app which was backed up by the RN app.

Testing: Run the RN app, make test actions like txs, tags, widgets and settings updates etc. Then save the backup mnemonic and delete the app. Then install this branch of the iOS app (make sure to run it with the same network of the RN app used). and restore wallet using the mnemonic. All old data should show up.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for restoring iOS app state from backups created by the React Native version of the app. The restore process intelligently selects the most recent backup between RN remote backups and existing VSS backups.

Key changes:

  • Implements RN remote backup client with authentication and decryption
  • Adds restore logic that compares RN and VSS backup timestamps to select the most recent
  • Migrates RN backup data including settings, widgets, activities, metadata, wallet state, and LDK channel data

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Bitkit/Services/RNBackupClient.swift New client for authenticating with and retrieving encrypted backups from RN backup server
Bitkit/Services/MigrationsService.swift Adds RN remote backup restore functionality and metadata reapplication logic
Bitkit/Utilities/Crypto.swift Implements Lightning Network message signing and public key derivation for backup authentication
Bitkit/AppScene.swift Implements backup timestamp comparison and restore orchestration during wallet restoration
Bitkit/ViewModels/AppViewModel.swift Adds listener for RN remote backup restoration completion
Bitkit/Services/CoreService.swift Fixes activity timestamp logic to use block timestamp when available
Bitkit/Constants/Env.swift Adds RN backup server endpoints and public keys
Bitkit/Utilities/AppReset.swift Prevents RN migration from triggering after app wipe
Bitkit.xcodeproj/project.pbxproj Adds RNBackupClient.swift to project

else { continue }

let ts = UInt64(timestamp / 1000) // Convert ms to seconds
if latestTimestamp == nil || ts > latestTimestamp! {
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

Avoid force unwrapping with !. Use optional binding or the nil-coalescing operator instead. For example: if let latest = latestTimestamp, ts <= latest { continue } or latestTimestamp = max(latestTimestamp ?? 0, ts).

Suggested change
if latestTimestamp == nil || ts > latestTimestamp! {
if let latest = latestTimestamp {
if ts > latest {
latestTimestamp = ts
}
} else {

Copilot uses AI. Check for mistakes.
extension MigrationsService {
func hasRNRemoteBackup(mnemonic: String, passphrase: String?) async -> Bool {
do {
let effectivePassphrase = passphrase?.isEmpty == true ? nil : passphrase
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

This passphrase normalization logic is duplicated in lines 1204 and 1215. Consider extracting it into a helper method to avoid repetition.

Copilot uses AI. Check for mistakes.
Comment on lines +416 to +420
let activityTimestamp: UInt64 = if existingActivity == nil, let bts = blockTimestamp, bts < paymentTimestamp {
bts
} else {
existingOnchain?.timestamp ?? paymentTimestamp
}
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

This timestamp selection logic is complex and would benefit from a comment explaining when block timestamp vs payment timestamp is used and why the condition checks for existingActivity == nil.

Copilot uses AI. Check for mistakes.
Comment on lines +353 to +357

// Determine which backup is more recent
let shouldRestoreRN: Bool = {
guard hasRNBackup else { return false }
guard let vss = vssTimestamp, vss > 0 else { return true } // No VSS, use RN
Copy link

Copilot AI Dec 26, 2025

Choose a reason for hiding this comment

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

The vss > 0 check appears to be a magic number. Consider using a named constant or adding a comment explaining what a timestamp of 0 or less represents.

Suggested change
// Determine which backup is more recent
let shouldRestoreRN: Bool = {
guard hasRNBackup else { return false }
guard let vss = vssTimestamp, vss > 0 else { return true } // No VSS, use RN
// A VSS timestamp of 0 indicates that no VSS backup exists.
let vssNoBackupTimestamp: UInt64 = 0
// Determine which backup is more recent
let shouldRestoreRN: Bool = {
guard hasRNBackup else { return false }
guard let vss = vssTimestamp, vss > vssNoBackupTimestamp else { return true } // No VSS, use RN

Copilot uses AI. Check for mistakes.
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