Skip to content

Commit

Permalink
Refactor restore controllers.
Browse files Browse the repository at this point in the history
- Add cases and temporary fix cloud oneWallet restore
  • Loading branch information
ant013 committed Oct 9, 2023
1 parent 194623e commit ee0e75c
Show file tree
Hide file tree
Showing 34 changed files with 754 additions and 513 deletions.
248 changes: 152 additions & 96 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -208,27 +208,31 @@ extension CloudBackupManager {
try delete(uniqueId: hex)
}

func delete(uniqueId: String) throws {
func delete(name: String) throws {
guard let iCloudUrl else {
throw BackupError.urlNotAvailable
}

guard let item = oneWalletItems.first(where: { _, backup in backup.id == uniqueId }) else {
throw BackupError.itemNotFound
}

let fileUrl = iCloudUrl.appendingPathComponent(item.key)
let fileUrl = iCloudUrl.appendingPathComponent(name)
do {
try fileStorage.deleteFile(url: fileUrl)

// system will automatically updates items but after 1-2 seconds. So we need force update
oneWalletItems[item.key] = nil
logger?.log(level: .debug, message: "CloudAccountManager.delete \(item.key) successful")
oneWalletItems[name] = nil
logger?.log(level: .debug, message: "CloudAccountManager.delete \(name) successful")
} catch {
logger?.log(level: .debug, message: "CloudAccountManager.delete \(item.key) unsuccessful because: \(error)")
logger?.log(level: .debug, message: "CloudAccountManager.delete \(name) unsuccessful because: \(error)")
throw error
}
}

func delete(uniqueId: String) throws {
guard let item = oneWalletItems.first(where: { _, backup in backup.id == uniqueId }) else {
throw BackupError.itemNotFound
}

try delete(name: item.key)
}
}

extension CloudBackupManager {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Foundation
import CloudKit
import Combine
import RxSwift
import RxRelay
import ObjectMapper
import Foundation
import HsToolKit
import MarketKit
import ObjectMapper
import RxRelay
import RxSwift

class ContactBookManager {
static private let batchingInterval: TimeInterval = 1
private static let batchingInterval: TimeInterval = 1
static let filename = "Contacts.json"

static let localUrl = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
Expand Down Expand Up @@ -39,7 +39,7 @@ class ContactBookManager {
private var metadataMonitor: MetadataMonitor?

private let iCloudErrorRelay = BehaviorRelay<Error?>(value: nil)
private(set) var iCloudError: Error? = nil {
private(set) var iCloudError: Error? {
didSet {
iCloudErrorRelay.accept(iCloudError)
}
Expand All @@ -57,9 +57,9 @@ class ContactBookManager {
let localUrl: URL?
var iCloudUrl: URL? {
FileManager
.default
.url(forUbiquityContainerIdentifier: ubiquityContainerIdentifier)?
.appendingPathComponent("Documents")
.default
.url(forUbiquityContainerIdentifier: ubiquityContainerIdentifier)?
.appendingPathComponent("Documents")
}

private var needsToSyncRemote = false {
Expand All @@ -83,7 +83,7 @@ class ContactBookManager {
syncLocalFile()
}

// ================================ LOCAL ==================================================== //
// ================================ LOCAL ==================================================== //
func syncLocalFile() {
state = .loading
guard let localUrl else {
Expand All @@ -94,7 +94,7 @@ class ContactBookManager {
logger?.debug("=C-MANAGER> SYNC")
do {
let data = try fileStorage
.read(directoryUrl: localUrl, filename: Self.filename)
.read(directoryUrl: localUrl, filename: Self.filename)
logger?.debug("=C-MANAGER> FOUND LOCAL: \(data.count)")
sync(localData: data)
} catch {
Expand Down Expand Up @@ -137,8 +137,8 @@ class ContactBookManager {
let localError = localError as NSError // code = 260 "No such file or directory"

if localError.domain == NSCocoaErrorDomain,
localError.code == 260 {

localError.code == 260
{
sync(localData: Data())
return
}
Expand All @@ -147,7 +147,7 @@ class ContactBookManager {
state = .failed(localError)
}

// ================================== REMOTE =================================================== //
// ================================== REMOTE =================================================== //
private func syncCloudFile(localBook: ContactBook) {
if let iCloudUrl {
logger?.debug("=C-MANAGER> Try read remote book")
Expand Down Expand Up @@ -209,7 +209,7 @@ class ContactBookManager {
logger?.debug("=C-MANAGER> Remote book is up to date. Save to local")
try save(url: localUrl, remoteContactBook)
state = .completed(remoteContactBook)
case .merged(let book):
case let .merged(book):
logger?.debug("=C-MANAGER> Merged. Save to both")
try save(url: localUrl, book)
state = .completed(book)
Expand All @@ -230,8 +230,8 @@ class ContactBookManager {
let iCloudError = iCloudError as NSError

if iCloudError.domain == NSCocoaErrorDomain,
iCloudError.code == 260 { // code = 260 "No such file or directory"

iCloudError.code == 260
{ // code = 260 "No such file or directory"
logger?.debug("=C-MANAGER> no file in icloud. Try to save local to icloud")
// we need to try save local contacts to iCloud file
if !localBook.contacts.isEmpty {
Expand Down Expand Up @@ -268,17 +268,16 @@ class ContactBookManager {
iCloudError = nil

if localStorage.remoteContactsSync {

// create monitor and handle its events
let metadataMonitor = MetadataMonitor(url: iCloudUrl, filenames: [Self.filename], batchingInterval: Self.batchingInterval, logger: logger)
self.metadataMonitor = metadataMonitor
logger?.debug("=C-MANAGER> Turn ON monitor")
metadataMonitor.needUpdatePublisher
.sink { [weak self] in
self?.logger?.debug("=C-MANAGER> Monitor Want to Sync iCloudStorage")
self?.syncRemoteStorage()
}
.store(in: &monitorCancellables)
.sink { [weak self] in
self?.logger?.debug("=C-MANAGER> Monitor Want to Sync iCloudStorage")
self?.syncRemoteStorage()
}
.store(in: &monitorCancellables)

syncRemoteStorage() // sometimes monitor not ask to check icloud file, but we need to check it for first time
} else {
Expand All @@ -296,9 +295,9 @@ class ContactBookManager {
}

// try to create json with parsed data
guard let json = try JSONSerialization.jsonObject(with: data) as? [String : Any] else {
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
logger?.debug("=C-MANAGER> CANT PARSE")
throw StorageError.cantParseData
throw StorageError.cantParseData
}

let book = try Mapper<ContactBook>().map(JSON: json)
Expand Down Expand Up @@ -328,11 +327,9 @@ class ContactBookManager {
state = .failed(error)
}
}

}

extension ContactBookManager {

var stateObservable: Observable<DataStatus<ContactBook>> {
stateRelay.asObservable()
}
Expand All @@ -356,7 +353,7 @@ extension ContactBookManager {
func name(blockchainType: BlockchainType, address: String) -> String? {
if let contact = all?.first(where: { contact in
!contact.addresses
.filter({ $0.blockchainUid == blockchainType.uid && $0.address.lowercased() == address.lowercased() }).isEmpty
.filter { $0.blockchainUid == blockchainType.uid && $0.address.lowercased() == address.lowercased() }.isEmpty
}) {
return contact.name
}
Expand All @@ -380,7 +377,6 @@ extension ContactBookManager {
if remoteSync {
try saveToICloud(book: newContactBook)
}

}

func delete(_ contactUid: String) throws {
Expand All @@ -406,23 +402,15 @@ extension ContactBookManager {
func backupContacts(from url: URL) throws -> [BackupContact] {
let data = try FileManager.default.contentsOfFile(coordinatingAccessAt: url)

guard let json = try? JSONSerialization.jsonObject(with: data) as? [[String : Any]],
let contacts = try? json.map({ try Mapper<BackupContact>().map(JSON: $0) }) else {

guard let json = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]],
let contacts = try? json.map({ try Mapper<BackupContact>().map(JSON: $0) })
else {
throw StorageError.cantParseData
}

return contacts
}

func restore(crypto: BackupCrypto, passphrase: String) throws {
let data = try crypto.data(passphrase: passphrase)
let decoder = JSONDecoder()
let contacts = try decoder.decode([BackupContact].self, from: data)

try restore(contacts: contacts)
}

func restore(contacts: [BackupContact]) throws {
guard let localUrl else {
state = .failed(ContactBookManager.StorageError.localUrlNotAvailable)
Expand All @@ -440,16 +428,23 @@ extension ContactBookManager {
var backupContactBook: BackupContactBook? {
state.data.map { helper.backupContactBook(contactBook: $0) }
}

}

extension ContactBookManager {
static func encode(crypto: BackupCrypto, passphrase: String) throws -> [BackupContact] {
let data = try crypto.data(passphrase: passphrase)
let decoder = JSONDecoder()
let contacts = try decoder.decode([BackupContact].self, from: data)

return contacts
}
}

extension ContactBookManager {
enum StorageError: Error {
case cloudUrlNotAvailable
case localUrlNotAvailable
case notReady
case cantParseData
}

}
7 changes: 7 additions & 0 deletions UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ extension AccountType {
case .cex: self = .cex
}
}

var isWatch: Bool {
switch self {
case .evmAddress, .tronAddress: return true
default: return false
}
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,34 @@ struct BackupModule {
}

}

extension BackupModule {
enum Source {
case wallet(WalletBackup)
case full(FullBackup)

enum Abstract {
case wallet
case full
}

var id: String {
switch self {
case let .wallet(backup): return backup.id
case let .full(backup): return backup.id
}
}

var timestamp: TimeInterval? {
switch self {
case let .wallet(backup): return backup.timestamp
case let .full(backup): return backup.timestamp
}
}
}

struct NamedSource {
let name: String
let source: Source
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ extension AppBackupProvider {
favoritesManager.add(coinUids: backup.watchlistIds)

if let contacts = backup.contacts {
try contactManager.restore(crypto: contacts, passphrase: passphrase)
let contacts = try ContactBookManager.encode(crypto: contacts, passphrase: passphrase)
try contactManager.restore(contacts: contacts)
}

if let settings = backup.settings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class ManageAccountsViewController: ThemeViewController {
}

private func onTapRestore() {
let viewController = RestoreTypeModule.viewController(sourceViewController: self, returnViewController: createAccountListener)
let viewController = RestoreTypeModule.viewController(type: .wallet, sourceViewController: self, returnViewController: createAccountListener)
present(viewController, animated: true)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ struct RestoreCloudModule {
}
}

struct DecryptedRestoredBackup {
let name: String
let walletBackup: WalletBackup
}

struct RestoredAccount {
let name: String
let accountType: AccountType
Expand Down

This file was deleted.

Loading

0 comments on commit ee0e75c

Please sign in to comment.