diff --git a/apps/ios/Koins/Bridge/KoinsMoneroWallet.mm b/apps/ios/Koins/Bridge/KoinsMoneroWallet.mm index ddea5a8..8256fb1 100644 --- a/apps/ios/Koins/Bridge/KoinsMoneroWallet.mm +++ b/apps/ios/Koins/Bridge/KoinsMoneroWallet.mm @@ -14,7 +14,22 @@ static std::string walletPath(NSString *name) { return std::string([dir stringByAppendingPathComponent:name].UTF8String); } +// Keep wallet files out of iCloud/iTunes backups: the seed in the Keychain is +// device-only and the file is rebuildable from it, so backing it up would only +// consume the user's quota (it peaks near 120 MB while a wallet restored from +// genesis scans). Must run after every write, since wallet2 rewrites these +// files on each save. +static void excludeWalletFilesFromBackup(const std::string &walletPathString) { + NSString *base = [NSString stringWithUTF8String:walletPathString.c_str()]; + if (base.length == 0) return; + for (NSString *suffix in @[ @"", @".keys", @".address.txt", @".mms" ]) { + NSURL *url = [NSURL fileURLWithPath:[base stringByAppendingString:suffix]]; + [url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil]; + } +} + static monero_wallet_full *g_wallet = nullptr; +static std::string g_walletPath; static NSString *g_daemonURL = @"xmr-node.cakewallet.com:18081"; static NSArray *g_fallbackDaemons = nil; static std::atomic s_cachedConnected(false); @@ -82,6 +97,8 @@ static void connectDaemon(monero_wallet_full *w) { connectDaemon(g_wallet); resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()]; try { resp[@"mnemonic"] = [NSString stringWithUTF8String:g_wallet->get_seed().c_str()]; } catch (...) {} + g_walletPath = path; + excludeWalletFilesFromBackup(g_walletPath); } else { monero_wallet_config config; config.m_path = path; @@ -94,6 +111,8 @@ static void connectDaemon(monero_wallet_full *w) { connectDaemon(w); resp[@"address"] = [NSString stringWithUTF8String:w->get_address(0, 0).c_str()]; resp[@"mnemonic"] = [NSString stringWithUTF8String:w->get_seed().c_str()]; + g_walletPath = path; + excludeWalletFilesFromBackup(g_walletPath); } } else if ([op isEqualToString:@"openWallet"] || [op isEqualToString:@"restoreWallet"]) { if (g_wallet) { @@ -118,6 +137,8 @@ static void connectDaemon(monero_wallet_full *w) { g_wallet = monero_wallet_full::create_wallet(config, nullptr); } connectDaemon(g_wallet); + g_walletPath = walletPath(req[@"name"]); + excludeWalletFilesFromBackup(g_walletPath); resp[@"address"] = [NSString stringWithUTF8String:g_wallet->get_address(0, 0).c_str()]; } else if ([op isEqualToString:@"getSeed"]) { if (g_wallet) { @@ -145,12 +166,14 @@ static void connectDaemon(monero_wallet_full *w) { delete g_wallet; g_wallet = nullptr; } + g_walletPath.clear(); } else if ([op isEqualToString:@"saveWallet"]) { // monero-cpp never persists while syncing, so scan progress survives a // restart only if this is called explicitly. save() pauses the sync // thread while it writes. if (g_wallet) { g_wallet->save(); + excludeWalletFilesFromBackup(g_walletPath); resp[@"saved"] = @YES; } else { resp[@"error"] = @"No wallet open";