diff --git a/.gitignore b/.gitignore
index c8e1b674a15d27d9b158ddfd077cdd45b0b16339..839d127c8fff949c405a77d770e8f22feb8b2b6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,5 +29,8 @@ xcuserdata/
 *.dSYM.zip
 *.dSYM
 *.p8
-*/cert_mainnet
-*/GoogleService-Info.plist
+Sources/Integration/Resources/udContact-test.bin
+Sources/Integration/Resources/ud.elixxir.io.crt
+Sources/GoogleDriveFeature/Resources/GoogleDrive-Keys.plist
+Sources/DropboxFeature/Resources/Dropbox-Keys.plist
+App/client-ios/Resources/GoogleService-Info.plist
\ No newline at end of file
diff --git a/App/NotificationExtension/NotificationService.swift b/App/NotificationExtension/NotificationService.swift
index 7a733b0d02dc77c886c246c63b6c1e0934c0231c..1c92ce4a7750684edcf92cbf933f616c89881f13 100644
--- a/App/NotificationExtension/NotificationService.swift
+++ b/App/NotificationExtension/NotificationService.swift
@@ -68,6 +68,8 @@ class NotificationService: UNNotificationServiceExtension {
                     content.title = "New media received"
                 case "groupRq":
                     content.title = "Group request received"
+                case "reset":
+                    content.title = "One of your contacts has restored their account"
                 default:
                     break
                 }
diff --git a/App/client-ios.xcodeproj/project.pbxproj b/App/client-ios.xcodeproj/project.pbxproj
index e7eaeac4bfeb69bf04b15ccdc40ad51676a77f62..76f38ed41ee371a895528771b590f85c69276b6c 100644
--- a/App/client-ios.xcodeproj/project.pbxproj
+++ b/App/client-ios.xcodeproj/project.pbxproj
@@ -15,6 +15,8 @@
 		32179BAC26410149008B26EC /* NotificationExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 32179BA526410149008B26EC /* NotificationExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		3273327126C7391D0027D79D /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 3273327026C7391D0027D79D /* App */; };
 		32824C8F26EAE13D005D3FAC /* Bindings in Frameworks */ = {isa = PBXBuildFile; productRef = 32824C8E26EAE13D005D3FAC /* Bindings */; };
+		32C194E02808C65500876917 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 32C194DF2808C65500876917 /* GoogleService-Info.plist */; };
+		32C194E12808C65500876917 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 32C194DF2808C65500876917 /* GoogleService-Info.plist */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -53,6 +55,7 @@
 		32179BA526410149008B26EC /* NotificationExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		32179BA726410149008B26EC /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
 		32179BA926410149008B26EC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		32C194DF2808C65500876917 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
 		32DB0549264DD42000FDCCEB /* NotificationExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationExtension.entitlements; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -82,6 +85,7 @@
 			children = (
 				02CB8FEB2326B5BE00A39834 /* client-ios.entitlements */,
 				02FDD06C21EDA39B000F1286 /* Assets.xcassets */,
+				32C194DF2808C65500876917 /* GoogleService-Info.plist */,
 				02FDD07121EDA39B000F1286 /* Info.plist */,
 			);
 			path = Resources;
@@ -145,8 +149,8 @@
 				02FDD05E21EDA39A000F1286 /* Sources */,
 				02FDD05F21EDA39A000F1286 /* Frameworks */,
 				02FDD06021EDA39A000F1286 /* Resources */,
-				028B1320260A99C800054510 /* ShellScript */,
 				32179BAD26410149008B26EC /* Embed App Extensions */,
+				3289935B27DB3EEF003E1407 /* ShellScript */,
 			);
 			buildRules = (
 			);
@@ -168,7 +172,6 @@
 				32179BA126410149008B26EC /* Sources */,
 				32179BA226410149008B26EC /* Frameworks */,
 				32179BA326410149008B26EC /* Resources */,
-				32151006264CAE8700A0F50D /* ShellScript */,
 			);
 			buildRules = (
 			);
@@ -234,6 +237,7 @@
 			files = (
 				02FDD07021EDA39B000F1286 /* LaunchScreen.storyboard in Resources */,
 				02FDD06D21EDA39B000F1286 /* Assets.xcassets in Resources */,
+				32C194E02808C65500876917 /* GoogleService-Info.plist in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -241,13 +245,14 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				32C194E12808C65500876917 /* GoogleService-Info.plist in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		028B1320260A99C800054510 /* ShellScript */ = {
+		3289935B27DB3EEF003E1407 /* ShellScript */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -262,24 +267,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "$SRCROOT/set_build_number.sh\n";
-		};
-		32151006264CAE8700A0F50D /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "$SRCROOT/set_build_number.sh\n";
+			shellScript = "${BUILD_DIR%Build/*}SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 
@@ -368,6 +356,7 @@
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"DEBUG=1",
 					"$(inherited)",
+					"SWIFT_PACKAGE=1",
 				);
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -381,6 +370,7 @@
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_SWIFT_FLAGS = "";
 				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SWIFT_PACKAGE DEBUG";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 			};
@@ -427,6 +417,7 @@
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = "SWIFT_PACKAGE=1";
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 				GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -438,6 +429,7 @@
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE;
 				SWIFT_COMPILATION_MODE = wholemodule;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				SWIFT_VERSION = 5.0;
@@ -456,7 +448,7 @@
 				CODE_SIGN_ENTITLEMENTS = "client-ios/Resources/client-ios.entitlements";
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 48;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
@@ -471,7 +463,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.0.5;
+				MARKETING_VERSION = 1.0.7;
 				OTHER_SWIFT_FLAGS = "$(inherited)";
 				PRODUCT_BUNDLE_IDENTIFIER = xx.messenger.mock;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -495,7 +487,7 @@
 				CODE_SIGN_ENTITLEMENTS = "client-ios/Resources/client-ios.entitlements";
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 48;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
@@ -511,7 +503,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.0.5;
+				MARKETING_VERSION = 1.0.7;
 				OTHER_SWIFT_FLAGS = "";
 				PRODUCT_BUNDLE_IDENTIFIER = xx.messenger;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -530,7 +522,7 @@
 				CODE_SIGN_ENTITLEMENTS = NotificationExtension/NotificationExtension.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 48;
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
@@ -544,7 +536,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 1.0.5;
+				MARKETING_VERSION = 1.0.7;
 				PRODUCT_BUNDLE_IDENTIFIER = xx.messenger.mock.notifications;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
@@ -561,7 +553,7 @@
 				CODE_SIGN_ENTITLEMENTS = NotificationExtension/NotificationExtension.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 48;
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
@@ -575,7 +567,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 1.0.5;
+				MARKETING_VERSION = 1.0.7;
 				PRODUCT_BUNDLE_IDENTIFIER = xx.messenger.notifications;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/App/client-ios/Resources/GoogleService-Info.plist b/App/client-ios/Resources/GoogleService-Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..03e09469daae0502a5202f6e63aca9db140d1d77
--- /dev/null
+++ b/App/client-ios/Resources/GoogleService-Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CLIENT_ID</key>
+	<string></string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string></string>
+	<key>ANDROID_CLIENT_ID</key>
+	<string></string>
+	<key>API_KEY</key>
+	<string></string>
+	<key>GCM_SENDER_ID</key>
+	<string></string>
+	<key>PLIST_VERSION</key>
+	<string></string>
+	<key>BUNDLE_ID</key>
+	<string></string>
+	<key>PROJECT_ID</key>
+	<string></string>
+	<key>STORAGE_BUCKET</key>
+	<string></string>
+	<key>IS_ADS_ENABLED</key>
+	<false/>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false/>
+	<key>IS_APPINVITE_ENABLED</key>
+	<false/>
+	<key>IS_GCM_ENABLED</key>
+	<false/>
+	<key>IS_SIGNIN_ENABLED</key>
+	<false/>
+	<key>GOOGLE_APP_ID</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/App/client-ios/Resources/Info.plist b/App/client-ios/Resources/Info.plist
index 100e1405c342feb92325536fc967cc83456bacc5..ed25d86cf62d659ee34ae0f720d9de7f8a56fefb 100644
--- a/App/client-ios/Resources/Info.plist
+++ b/App/client-ios/Resources/Info.plist
@@ -24,6 +24,14 @@
 	<string>$(MARKETING_VERSION)</string>
 	<key>CFBundleURLTypes</key>
 	<array>
+		<dict>
+			<key>CFBundleURLName</key>
+			<string></string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>db-ppx0de5f16p9aq2</string>
+			</array>
+		</dict>
 		<dict>
 			<key>CFBundleURLName</key>
 			<string>xxmessenger</string>
@@ -32,6 +40,14 @@
 				<string>xxmessenger</string>
 			</array>
 		</dict>
+		<dict>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.googleusercontent.apps.662236151640-30i07ubg6ukodg15u0bnpk322p030u3j</string>
+			</array>
+		</dict>
 	</array>
 	<key>CFBundleVersion</key>
 	<string>$(CURRENT_PROJECT_VERSION)</string>
@@ -39,7 +55,8 @@
 	<false/>
 	<key>LSApplicationQueriesSchemes</key>
 	<array>
-		<string>prelixxir</string>
+		<string>dbapi-8-emm</string>
+		<string>dbapi-2</string>
 	</array>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
@@ -58,6 +75,18 @@
 	<string>This permission is required to create voice notes</string>
 	<key>NSPhotoLibraryUsageDescription</key>
 	<string>This permission is required to send photos in chat and change your profile avatar.</string>
+	<key>NSUbiquitousContainers</key>
+	<dict>
+		<key>iCloud.xxm-icloud</key>
+		<dict>
+			<key>NSUbiquitousContainerIsDocumentScopePublic</key>
+			<true/>
+			<key>NSUbiquitousContainerName</key>
+			<string>xx messenger</string>
+			<key>NSUbiquitousContainerSupportedFolderLevels</key>
+			<string>Any</string>
+		</dict>
+	</dict>
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>processing</string>
diff --git a/App/client-ios/Resources/client-ios.entitlements b/App/client-ios/Resources/client-ios.entitlements
index 711179f6d68ce953b1647593946d418411ea0422..ce90eb85306477465bf77307eed53d5434c6ba48 100644
--- a/App/client-ios/Resources/client-ios.entitlements
+++ b/App/client-ios/Resources/client-ios.entitlements
@@ -2,12 +2,24 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-	<key>com.apple.developer.usernotifications.filtering</key>
-	<true/>
 	<key>aps-environment</key>
 	<string>development</string>
+	<key>com.apple.developer.icloud-container-identifiers</key>
+	<array>
+		<string>iCloud.xxm-cloud</string>
+	</array>
+	<key>com.apple.developer.icloud-services</key>
+	<array>
+		<string>CloudDocuments</string>
+	</array>
 	<key>com.apple.developer.networking.multipath</key>
 	<true/>
+	<key>com.apple.developer.ubiquity-container-identifiers</key>
+	<array>
+		<string>iCloud.xxm-cloud</string>
+	</array>
+	<key>com.apple.developer.usernotifications.filtering</key>
+	<true/>
 	<key>com.apple.security.application-groups</key>
 	<array>
 		<string>group.io.xxlabs.notification</string>
diff --git a/Package.swift b/Package.swift
index 495a08601d5bc952358ca872dbe461c34fff922f..ed7f12f2320f58eff87fb47cfcecc45d369c8d83 100644
--- a/Package.swift
+++ b/Package.swift
@@ -30,11 +30,15 @@ let package = Package(
         .library(name: "ChatFeature", targets: ["ChatFeature"]),
         .library(name: "CrashService", targets: ["CrashService"]),
         .library(name: "Presentation", targets: ["Presentation"]),
+        .library(name: "BackupFeature", targets: ["BackupFeature"]),
+        .library(name: "iCloudFeature", targets: ["iCloudFeature"]),
         .library(name: "SearchFeature", targets: ["SearchFeature"]),
+        .library(name: "RestoreFeature", targets: ["RestoreFeature"]),
         .library(name: "CrashReporting", targets: ["CrashReporting"]),
         .library(name: "ProfileFeature", targets: ["ProfileFeature"]),
         .library(name: "ContactFeature", targets: ["ContactFeature"]),
         .library(name: "NetworkMonitor", targets: ["NetworkMonitor"]),
+        .library(name: "DropboxFeature", targets: ["DropboxFeature"]),
         .library(name: "VersionChecking", targets: ["VersionChecking"]),
         .library(name: "SettingsFeature", targets: ["SettingsFeature"]),
         .library(name: "ChatListFeature", targets: ["ChatListFeature"]),
@@ -42,6 +46,7 @@ let package = Package(
         .library(name: "ChatInputFeature", targets: ["ChatInputFeature"]),
         .library(name: "PushNotifications", targets: ["PushNotifications"]),
         .library(name: "OnboardingFeature", targets: ["OnboardingFeature"]),
+        .library(name: "GoogleDriveFeature", targets: ["GoogleDriveFeature"]),
         .library(name: "ContactListFeature", targets: ["ContactListFeature"]),
         .library(name: "DependencyInjection", targets: ["DependencyInjection"])
     ],
@@ -61,11 +66,26 @@ let package = Package(
             url: "https://github.com/Quick/Nimble",
             from: "9.0.0"
         ),
+        .package(
+            name: "FilesProvider",
+            url: "https://github.com/amosavian/FileProvider.git",
+            from: "0.26.0"
+        ),
         .package(
             name: "GRDB",
             url: "https://github.com/groue/GRDB.swift",
             from: "5.3.0"
         ),
+        .package(
+            name: "GoogleSignIn",
+            url: "https://github.com/google/GoogleSignIn-iOS",
+            from: "6.1.0"
+        ),
+        .package(
+            name: "GoogleAPIClientForREST",
+            url: "https://github.com/google/google-api-objectivec-client-for-rest",
+            from: "1.6.0"
+        ),
         .package(
             name: "SnapKit",
             url: "https://github.com/SnapKit/SnapKit",
@@ -81,6 +101,11 @@ let package = Package(
             url: "https://github.com/apple/swift-protobuf",
             from: "1.14.0"
         ),
+        .package(
+            name: "SwiftyDropbox",
+            url: "https://github.com/dropbox/SwiftyDropbox.git",
+            from: "8.2.1"
+        ),
         .package(
             name: "KeychainAccess",
             url: "https://github.com/kishikawakatsumi/KeychainAccess",
@@ -128,14 +153,19 @@ let package = Package(
                 "ChatFeature",
                 "MenuFeature",
                 "CrashService",
+                "BackupFeature",
                 "SearchFeature",
+                "iCloudFeature",
+                "DropboxFeature",
                 "ContactFeature",
+                "RestoreFeature",
                 "ProfileFeature",
                 "CrashReporting",
                 "ChatListFeature",
                 "SettingsFeature",
                 "PushNotifications",
                 "OnboardingFeature",
+                "GoogleDriveFeature",
                 "ContactListFeature"
             ]
         ),
@@ -236,6 +266,48 @@ let package = Package(
                 ]
             ),
 
+        // MARK: - GoogleDriveFeature
+
+            .target(
+                name: "GoogleDriveFeature",
+                dependencies: [
+                    .product(
+                        name: "GoogleSignIn",
+                        package: "GoogleSignIn"
+                    ),
+                    .product(
+                        name: "GoogleAPIClientForREST_Drive",
+                        package: "GoogleAPIClientForREST"
+                    )
+                ],
+                resources: [.process("Resources")]
+            ),
+
+        // MARK: - iCloudFeature
+
+            .target(
+                name: "iCloudFeature",
+                dependencies: [
+                    .product(
+                        name: "FilesProvider",
+                        package: "FilesProvider"
+                    )
+                ]
+            ),
+
+        // MARK: - DropboxFeature
+
+            .target(
+                name: "DropboxFeature",
+                dependencies: [
+                    .product(
+                        name: "SwiftyDropbox",
+                        package: "SwiftyDropbox"
+                    )
+                ],
+                resources: [.process("Resources")]
+            ),
+
         // MARK: - Countries
 
             .target(
@@ -346,6 +418,7 @@ let package = Package(
                     "Shared",
                     "Database",
                     "Bindings",
+                    "BackupFeature",
                     "CrashReporting",
                     "NetworkMonitor",
                     "DependencyInjection",
@@ -384,6 +457,22 @@ let package = Package(
                 ]
             ),
 
+        // MARK: - RestoreFeature
+
+            .target(
+                name: "RestoreFeature",
+                dependencies: [
+                    "HUD",
+                    "Shared",
+                    "Integration",
+                    "Presentation",
+                    "iCloudFeature",
+                    "DropboxFeature",
+                    "GoogleDriveFeature",
+                    "DependencyInjection"
+                ]
+            ),
+
         // MARK: - ContactFeature
 
             .target(
@@ -560,6 +649,22 @@ let package = Package(
                 ]
             ),
 
+        // MARK: - BackupFeature
+
+            .target(
+                name: "BackupFeature",
+                dependencies: [
+                    "HUD",
+                    "Shared",
+                    "Models",
+                    "Presentation",
+                    "GoogleDriveFeature",
+                    "iCloudFeature",
+                    "DropboxFeature",
+                    "DependencyInjection"
+                ]
+            ),
+
         // MARK: - ScanFeature
 
             .target(
diff --git a/Sources/App/AppDelegate.swift b/Sources/App/AppDelegate.swift
index b177f06784bfb4cde7d1eee1cc6ae7c86ee3771f..927bcf4d17dcb4b3825b36374390634effc168b3 100644
--- a/Sources/App/AppDelegate.swift
+++ b/Sources/App/AppDelegate.swift
@@ -6,17 +6,20 @@ import Theme
 import XXLogger
 import Defaults
 import Integration
+import SwiftyDropbox
 import CrashReporting
 import PushNotifications
 import DependencyInjection
 
 import OnboardingFeature
+import DropboxFeature
 
 let logger = Logger(subsystem: "logs_xxmessenger", category: "AppDelegate.swift")
 
 public class AppDelegate: UIResponder, UIApplicationDelegate {
     @Dependency private var pushHandler: PushHandling
     @Dependency private var crashReporter: CrashReporter
+    @Dependency private var dropboxService: DropboxInterface
 
     @KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool
     @KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool
@@ -148,6 +151,10 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
         application.applicationIconBadgeNumber = 0
         coverView?.removeFromSuperview()
     }
+
+    public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
+        dropboxService.handleOpenUrl(url)
+    }
 }
 
 // MARK: Notifications
diff --git a/Sources/App/DependencyRegistrator.swift b/Sources/App/DependencyRegistrator.swift
index 41b6eabd9459622ea764ebfc9bc8697fe58541b2..59b18eb00646812bbd93ee249335a2b65bd2d6bf 100644
--- a/Sources/App/DependencyRegistrator.swift
+++ b/Sources/App/DependencyRegistrator.swift
@@ -1,6 +1,9 @@
 // MARK: SDK
 
+import UIKit
 import Network
+import QuickLook
+import MobileCoreServices
 
 // MARK: Isolated features
 
@@ -10,22 +13,28 @@ import Bindings
 import XXLogger
 import Keychain
 import Defaults
+import Countries
+import Voxophone
 import Integration
 import Permissions
 import CrashService
+import iCloudFeature
 import CrashReporting
 import NetworkMonitor
+import DropboxFeature
 import VersionChecking
 import PushNotifications
+import GoogleDriveFeature
 import DependencyInjection
-import Voxophone
 
 // MARK: UI Features
 
 import ScanFeature
 import ChatFeature
 import MenuFeature
+import BackupFeature
 import SearchFeature
+import RestoreFeature
 import ContactFeature
 import ProfileFeature
 import ChatListFeature
@@ -49,21 +58,36 @@ struct DependencyRegistrator {
         container.register(MockPushHandler() as PushHandling)
         container.register(MockKeychainHandler() as KeychainHandling)
         container.register(MockPermissionHandler() as PermissionHandling)
+
+        /// Restore / Backup
+
+        container.register(iCloudServiceMock() as iCloudInterface)
+        container.register(DropboxServiceMock() as DropboxInterface)
+        container.register(GoogleDriveServiceMock() as GoogleDriveInterface)
+
         registerCommonDependencies()
     }
 
     // MARK: LIVE
 
     static func registerForLive() {
+        container.register(KeyObjectStore.userDefaults)
         container.register(XXLogger.live())
         container.register(CrashReporter.live)
         container.register(VersionChecker.live())
+
         container.register(XXNetwork<BindingsClient>() as XXNetworking)
         container.register(NetworkMonitor() as NetworkMonitoring)
-        container.register(KeyObjectStore.userDefaults)
         container.register(PushHandler() as PushHandling)
         container.register(KeychainHandler() as KeychainHandling)
         container.register(PermissionHandler() as PermissionHandling)
+
+        /// Restore / Backup
+
+        container.register(iCloudService() as iCloudInterface)
+        container.register(DropboxService() as DropboxInterface)
+        container.register(GoogleDriveService() as GoogleDriveInterface)
+
         registerCommonDependencies()
     }
 
@@ -71,6 +95,7 @@ struct DependencyRegistrator {
 
     static private func registerCommonDependencies() {
         container.register(Voxophone())
+        container.register(BackupService())
 
         // MARK: Isolated
 
@@ -80,29 +105,77 @@ struct DependencyRegistrator {
 
         // MARK: Coordinators
 
-        container.register(SearchCoordinator() as SearchCoordinating)
-        container.register(ProfileCoordinator() as ProfileCoordinating)
-        container.register(SettingsCoordinator() as SettingsCoordinating)
+        container.register(BackupCoordinator() as BackupCoordinating)
+
+        container.register(
+            SearchCoordinator(
+                contactFactory: ContactController.init(_:),
+                countriesFactory: CountryListController.init(_:)
+            ) as SearchCoordinating)
+
+        container.register(
+            ProfileCoordinator(
+                emailFactory: ProfileEmailController.init,
+                phoneFactory: ProfilePhoneController.init,
+                imagePickerFactory: UIImagePickerController.init,
+                permissionFactory: RequestPermissionController.init,
+                countriesFactory: CountryListController.init(_:),
+                codeFactory: ProfileCodeController.init(_:_:)
+            ) as ProfileCoordinating)
+
+        container.register(
+            SettingsCoordinator(
+                backupFactory: BackupController.init,
+                advancedFactory: SettingsAdvancedController.init,
+                accountDeleteFactory: AccountDeleteController.init
+            ) as SettingsCoordinating)
+
+        container.register(
+            RestoreCoordinator(
+                successFactory: RestoreSuccessController.init,
+                chatListFactory: ChatListController.init,
+                restoreFactory: RestoreController.init(_:_:)
+            ) as RestoreCoordinating)
 
         container.register(
             ChatCoordinator(
                 retryFactory: RetrySheetController.init,
-                contactFactory: ContactController.init(_:)
+                webFactory: WebScreen.init(url:),
+                previewFactory: QLPreviewController.init,
+                contactFactory: ContactController.init(_:),
+                imagePickerFactory: UIImagePickerController.init,
+                permissionFactory: RequestPermissionController.init
             ) as ChatCoordinating)
 
         container.register(
             ContactCoordinator(
-                requestsFactory: RequestsContainerController.init
+                requestsFactory: RequestsContainerController.init,
+                singleChatFactory: SingleChatController.init(_:),
+                imagePickerFactory: UIImagePickerController.init,
+                nicknameFactory: NickameController.init(_:_:)
             ) as ContactCoordinating)
 
         container.register(
             RequestsCoordinator(
-                searchFactory: SearchController.init
+                searchFactory: SearchController.init,
+                verifyingFactory: VerifyingController.init,
+                contactFactory: ContactController.init(_:),
+                nicknameFactory: NickameController.init(_:_:)
             ) as RequestsCoordinating)
 
         container.register(
             OnboardingCoordinator(
-                chatListFactory: ChatListController.init
+                emailFactory: OnboardingEmailController.init,
+                phoneFactory: OnboardingPhoneController.init,
+                welcomeFactory: OnboardingWelcomeController.init,
+                chatListFactory: ChatListController.init,
+                startFactory: OnboardingStartController.init(_:),
+                usernameFactory: OnboardingUsernameController.init(_:),
+                restoreListFactory: RestoreListController.init(_:),
+                successFactory: OnboardingSuccessController.init(_:),
+                countriesFactory: CountryListController.init(_:),
+                phoneConfirmationFactory: OnboardingPhoneConfirmationController.init(_:_:),
+                emailConfirmationFactory: OnboardingEmailConfirmationController.init(_:_:)
             ) as OnboardingCoordinating)
 
         container.register(
@@ -110,13 +183,17 @@ struct DependencyRegistrator {
                 scanFactory: ScanContainerController.init,
                 searchFactory: SearchController.init,
                 newGroupFactory: CreateGroupController.init,
-                requestsFactory: RequestsContainerController.init
+                requestsFactory: RequestsContainerController.init,
+                contactFactory: ContactController.init(_:),
+                groupChatFactory: GroupChatController.init(_:),
+                groupPopupFactory: CreatePopupController.init(_:_:)
             ) as ContactListCoordinating)
 
         container.register(
             ScanCoordinator(
                 contactsFactory: ContactListController.init,
-                requestsFactory: RequestsContainerController.init
+                requestsFactory: RequestsContainerController.init,
+                contactFactory: ContactController.init(_:)
             ) as ScanCoordinating)
 
         container.register(
@@ -126,7 +203,10 @@ struct DependencyRegistrator {
                 profileFactory: ProfileController.init,
                 settingsFactory: SettingsController.init,
                 contactsFactory: ContactListController.init,
-                requestsFactory: RequestsContainerController.init
+                requestsFactory: RequestsContainerController.init,
+                singleChatFactory: SingleChatController.init(_:),
+                sideMenuFactory: MenuController.init(_:),
+                groupChatFactory: GroupChatController.init(_:)
             ) as ChatListCoordinating)
     }
 }
diff --git a/Sources/BackupFeature/Controllers/BackupConfigController.swift b/Sources/BackupFeature/Controllers/BackupConfigController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..aa48d9e1db6874afb18d2dcb0d29519497c3e1e8
--- /dev/null
+++ b/Sources/BackupFeature/Controllers/BackupConfigController.swift
@@ -0,0 +1,297 @@
+import UIKit
+import Popup
+import Models
+import Shared
+import Combine
+import DependencyInjection
+
+final class BackupConfigController: UIViewController {
+    @Dependency private var coordinator: BackupCoordinating
+
+    lazy private var screenView = BackupConfigView()
+
+    private let viewModel: BackupConfigViewModel
+    private var cancellables = Set<AnyCancellable>()
+    private var popupCancellables = Set<AnyCancellable>()
+
+    private var wifiOnly = false
+    private var manualBackups = false
+    private var serviceName: String = ""
+
+    override func loadView() {
+        view = screenView
+    }
+
+    init(_ viewModel: BackupConfigViewModel) {
+        self.viewModel = viewModel
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupBindings()
+    }
+
+    private func setupBindings() {
+        viewModel.actionState()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in screenView.actionView.setState($0) }
+            .store(in: &cancellables)
+
+        viewModel.connectedServices()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in decorate(connectedServices: $0) }
+            .store(in: &cancellables)
+
+        viewModel.enabledService()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in decorate(enabledService: $0) }
+            .store(in: &cancellables)
+
+        viewModel.automatic()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in
+                screenView.frequencyDetailView.subtitleLabel.text = $0 ? "Automatic" : "Manual"
+                manualBackups = !$0
+            }.store(in: &cancellables)
+
+        viewModel.wifiOnly()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in
+                screenView.infrastructureDetailView.subtitleLabel.text = $0 ? "Wi-Fi Only" : "Wi-Fi and Cellular"
+                wifiOnly = $0
+            }.store(in: &cancellables)
+
+        viewModel.lastBackup()
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in
+                guard let backup = $0 else {
+                    screenView.latestBackupDetailView.subtitleLabel.text = "Never"
+                    return
+                }
+
+                screenView.latestBackupDetailView.subtitleLabel.text = backup.date.backupStyle()
+            }.store(in: &cancellables)
+
+        screenView.actionView.backupNowButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapBackupNow() }
+            .store(in: &cancellables)
+
+        screenView.frequencyDetailView
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in presentFrequencyPopup(manual: manualBackups) }
+            .store(in: &cancellables)
+
+        screenView.infrastructureDetailView
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in presentInfrastructurePopup(wifiOnly: wifiOnly) }
+            .store(in: &cancellables)
+
+        screenView.googleDriveButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.drive, self) }
+            .store(in: &cancellables)
+
+        screenView.googleDriveButton.switcherView
+            .publisher(for: .valueChanged)
+            .sink { [unowned self] in viewModel.didToggleService(.drive, screenView.googleDriveButton.switcherView.isOn) }
+            .store(in: &cancellables)
+
+        screenView.dropboxButton.switcherView
+            .publisher(for: .valueChanged)
+            .sink { [unowned self] in viewModel.didToggleService(.dropbox, screenView.dropboxButton.switcherView.isOn) }
+            .store(in: &cancellables)
+
+        screenView.iCloudButton.switcherView
+            .publisher(for: .valueChanged)
+            .sink { [unowned self] in viewModel.didToggleService(.icloud, screenView.iCloudButton.switcherView.isOn) }
+            .store(in: &cancellables)
+
+        screenView.dropboxButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.dropbox, self) }
+            .store(in: &cancellables)
+
+        screenView.iCloudButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.icloud, self) }
+            .store(in: &cancellables)
+    }
+
+    private func decorate(enabledService: CloudService?) {
+        var button: BackupSwitcherButton?
+
+        switch enabledService {
+        case .none:
+            break
+        case .icloud:
+            serviceName = "iCloud"
+            button = screenView.iCloudButton
+
+        case .dropbox:
+            serviceName = "Dropbox"
+            button = screenView.dropboxButton
+
+        case .drive:
+            serviceName = "Google Drive"
+            button = screenView.googleDriveButton
+        }
+
+        screenView.enabledSubtitleLabel.text
+        = Localized.Backup.Config.disclaimer(serviceName)
+        screenView.frequencyDetailView.titleLabel.text
+        = Localized.Backup.Config.frequency(serviceName).uppercased()
+
+        guard let button = button else {
+            screenView.iCloudButton.isHidden = false
+            screenView.dropboxButton.isHidden = false
+            screenView.googleDriveButton.isHidden = false
+
+            screenView.iCloudButton.switcherView.isOn = false
+            screenView.dropboxButton.switcherView.isOn = false
+            screenView.googleDriveButton.switcherView.isOn = false
+
+            screenView.frequencyDetailView.isHidden = true
+            screenView.enabledSubtitleView.isHidden = true
+            screenView.latestBackupDetailView.isHidden = true
+            screenView.infrastructureDetailView.isHidden = true
+            return
+        }
+
+        screenView.frequencyDetailView.isHidden = false
+        screenView.enabledSubtitleView.isHidden = false
+        screenView.latestBackupDetailView.isHidden = false
+        screenView.infrastructureDetailView.isHidden = false
+
+        [screenView.iCloudButton, screenView.dropboxButton, screenView.googleDriveButton]
+            .forEach {
+                $0.isHidden = $0 != button
+                $0.switcherView.isOn = $0 == button
+            }
+    }
+
+    private func decorate(connectedServices: Set<CloudService>) {
+        if connectedServices.contains(.icloud) {
+            screenView.iCloudButton.showSwitcher(enabled: false)
+        } else {
+            screenView.iCloudButton.showChevron()
+        }
+
+        if connectedServices.contains(.dropbox) {
+            screenView.dropboxButton.showSwitcher(enabled: false)
+        } else {
+            screenView.dropboxButton.showChevron()
+        }
+
+        if connectedServices.contains(.drive) {
+            screenView.googleDriveButton.showSwitcher(enabled: false)
+        } else {
+            screenView.googleDriveButton.showChevron()
+        }
+    }
+
+    private func presentInfrastructurePopup(wifiOnly: Bool) {
+        let cancelButton = CapsuleButton()
+        cancelButton.setStyle(.seeThrough)
+        cancelButton.setTitle(Localized.ChatList.Dashboard.cancel, for: .normal)
+
+        let wifiOnlyButton = PopupRadioButton(title: "Wi-Fi Only", isSelected: wifiOnly)
+        let wifiAndCellularButton = PopupRadioButton(title: "Wi-Fi and Cellular", isSelected: !wifiOnly)
+
+        let popup = BottomPopup(with: [
+            PopupLabel(
+                font: Fonts.Mulish.extraBold.font(size: 28.0),
+                text: Localized.Backup.Config.infrastructure,
+                color: Asset.neutralActive.color,
+                alignment: .left,
+                spacingAfter: 30
+            ),
+            wifiOnlyButton,
+            wifiAndCellularButton,
+            PopupEmptyView(height: 20.0),
+            PopupStackView(spacing: 20.0, views: [cancelButton])
+        ])
+
+        wifiOnlyButton.action
+            .sink { [unowned self] in
+                viewModel.didChooseWifiOnly(true)
+
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        wifiAndCellularButton.action
+            .sink { [unowned self] in
+                viewModel.didChooseWifiOnly(false)
+
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        cancelButton.publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink {
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        coordinator.toPopup(popup, from: self)
+    }
+
+    private func presentFrequencyPopup(manual: Bool) {
+        let cancelButton = CapsuleButton()
+        cancelButton.setStyle(.seeThrough)
+        cancelButton.setTitle(Localized.ChatList.Dashboard.cancel, for: .normal)
+
+        let manualButton = PopupRadioButton(title: "Manual", isSelected: manual)
+        let automaticButton = PopupRadioButton(title: "Automatic", isSelected: !manual)
+
+        let popup = BottomPopup(with: [
+            PopupLabel(
+                font: Fonts.Mulish.extraBold.font(size: 28.0),
+                text: Localized.Backup.Config.frequency(serviceName),
+                color: Asset.neutralActive.color,
+                alignment: .left,
+                spacingAfter: 30
+            ),
+            manualButton,
+            automaticButton,
+            PopupEmptyView(height: 20.0),
+            PopupStackView(spacing: 20.0, views: [cancelButton])
+        ])
+
+        manualButton.action
+            .sink { [unowned self] in
+                viewModel.didChooseAutomatic(false)
+
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        automaticButton.action
+            .sink { [unowned self] in
+                viewModel.didChooseAutomatic(true)
+
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        cancelButton.publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink {
+                popup.dismiss(animated: true) { [weak self] in
+                    self?.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        coordinator.toPopup(popup, from: self)
+    }
+}
diff --git a/Sources/BackupFeature/Controllers/BackupController.swift b/Sources/BackupFeature/Controllers/BackupController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..65ef336de786ffb0b62789f21c0059995d643591
--- /dev/null
+++ b/Sources/BackupFeature/Controllers/BackupController.swift
@@ -0,0 +1,77 @@
+import HUD
+import UIKit
+import Shared
+import Models
+import Combine
+import DependencyInjection
+
+public final class BackupController: UIViewController {
+    @Dependency private var hud: HUDType
+
+    private let viewModel = BackupViewModel.live()
+    private var cancellables = Set<AnyCancellable>()
+
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        view.backgroundColor = Asset.neutralWhite.color
+        hud.update(with: .on)
+
+        setupNavigationBar()
+        setupBindings()
+    }
+
+    private func setupNavigationBar() {
+        navigationItem.backButtonTitle = ""
+
+        let title = UILabel()
+        title.text = Localized.Backup.header
+        title.textColor = Asset.neutralActive.color
+        title.font = Fonts.Mulish.semiBold.font(size: 18.0)
+
+        let back = UIButton.back()
+        back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside)
+
+        navigationItem.leftBarButtonItem = UIBarButtonItem(
+            customView: UIStackView(arrangedSubviews: [back, title])
+        )
+    }
+
+    private func setupBindings() {
+        viewModel.state()
+            .receive(on: DispatchQueue.main)
+            .removeDuplicates()
+            .sink { [unowned self] in
+                hud.update(with: .none)
+
+                switch $0 {
+                case .setup:
+                    contentViewController = BackupSetupController(viewModel.setupViewModel())
+                case .config:
+                    contentViewController = BackupConfigController(viewModel.configViewModel())
+                }
+            }.store(in: &cancellables)
+    }
+
+    private var contentViewController: UIViewController? {
+        didSet {
+            guard contentViewController != oldValue else { return }
+
+            if let oldValue = oldValue {
+                oldValue.willMove(toParent: nil)
+                oldValue.view.removeFromSuperview()
+                oldValue.removeFromParent()
+            }
+
+            if let newValue = contentViewController {
+                addChild(newValue)
+                view.addSubview(newValue.view)
+                newValue.view.snp.makeConstraints { $0.edges.equalToSuperview() }
+                newValue.didMove(toParent: self)
+            }
+        }
+    }
+
+    @objc private func didTapBack() {
+        navigationController?.popViewController(animated: true)
+    }
+}
diff --git a/Sources/BackupFeature/Controllers/BackupSetupController.swift b/Sources/BackupFeature/Controllers/BackupSetupController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..49e05a2bd74ce9141e19320cfebbc33f989414f7
--- /dev/null
+++ b/Sources/BackupFeature/Controllers/BackupSetupController.swift
@@ -0,0 +1,41 @@
+import UIKit
+import Models
+import Combine
+import DependencyInjection
+
+final class BackupSetupController: UIViewController {
+    lazy private var screenView = BackupSetupView()
+
+    private let viewModel: BackupSetupViewModel
+    private var cancellables = Set<AnyCancellable>()
+
+    override func loadView() {
+        view = screenView
+    }
+
+    init(_ viewModel: BackupSetupViewModel) {
+        self.viewModel = viewModel
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        screenView.googleDriveButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.drive, self) }
+            .store(in: &cancellables)
+
+        screenView.dropboxButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.dropbox, self) }
+            .store(in: &cancellables)
+
+        screenView.iCloudButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapService(.icloud, self) }
+            .store(in: &cancellables)
+    }
+}
diff --git a/Sources/BackupFeature/Coordinator/BackupCoordinator.swift b/Sources/BackupFeature/Coordinator/BackupCoordinator.swift
new file mode 100644
index 0000000000000000000000000000000000000000..75181b7ce104d68e8f8b159a2b0069a725622cc6
--- /dev/null
+++ b/Sources/BackupFeature/Coordinator/BackupCoordinator.swift
@@ -0,0 +1,18 @@
+import UIKit
+import Presentation
+
+public protocol BackupCoordinating {
+    func toPopup(_: UIViewController, from: UIViewController)
+}
+
+public struct BackupCoordinator: BackupCoordinating {
+    var bottomPresenter: Presenting = BottomPresenter()
+
+    public init() {}
+}
+
+public extension BackupCoordinator {
+    func toPopup(_ screen: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(screen, from: parent)
+    }
+}
diff --git a/Sources/BackupFeature/Service/BackupService.swift b/Sources/BackupFeature/Service/BackupService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4c62ba50d557ff4271ccf29b2786df73f17206c8
--- /dev/null
+++ b/Sources/BackupFeature/Service/BackupService.swift
@@ -0,0 +1,296 @@
+import UIKit
+import Models
+import Combine
+import Defaults
+import iCloudFeature
+import DropboxFeature
+import NetworkMonitor
+import GoogleDriveFeature
+import DependencyInjection
+
+public final class BackupService {
+    @Dependency private var icloudService: iCloudInterface
+    @Dependency private var dropboxService: DropboxInterface
+    @Dependency private var driveService: GoogleDriveInterface
+    @Dependency private var networkManager: NetworkMonitoring
+
+    @KeyObject(.backupSettings, defaultValue: Data()) private var storedSettings: Data
+
+    public var settingsPublisher: AnyPublisher<BackupSettings, Never> {
+        settings.handleEvents(receiveSubscription: { [weak self] _ in
+            guard let self = self else { return }
+
+            let lastRefreshDate = self.settingsLastRefreshedDate ?? Date.distantPast
+
+            if Date().timeIntervalSince(lastRefreshDate) < 10 { return }
+
+            self.settingsLastRefreshedDate = Date()
+            self.refreshConnections()
+            self.refreshBackups()
+        }).eraseToAnyPublisher()
+    }
+
+    private var connType: ConnectionType = .wifi
+    private var settingsLastRefreshedDate: Date?
+    private var cancellables = Set<AnyCancellable>()
+    private lazy var settings = CurrentValueSubject<BackupSettings, Never>(.init(fromData: storedSettings))
+
+    public init() {
+        settings
+            .dropFirst()
+            .removeDuplicates()
+            .sink { [unowned self] in storedSettings = $0.toData() }
+            .store(in: &cancellables)
+
+        networkManager.connType
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in connType = $0 }
+            .store(in: &cancellables)
+    }
+}
+
+extension BackupService {
+    public func performBackupIfAutomaticIsEnabled() {
+        guard settings.value.automaticBackups == true else { return }
+        performBackup()
+    }
+
+    public func performBackup() {
+        guard let directoryUrl = try? FileManager.default.url(
+            for: .applicationSupportDirectory,
+            in: .userDomainMask,
+            appropriateFor: nil,
+            create: true
+        ) else { fatalError("Couldn't generate the URL to persist the backup") }
+
+        let fileUrl = directoryUrl
+            .appendingPathComponent("backup")
+            .appendingPathExtension("xxm")
+
+        guard let data = try? Data(contentsOf: fileUrl) else {
+            print(">>> Tried to backup arbitrarily but there was nothing to be backed up. Aborting...")
+            return
+        }
+
+        performBackup(data: data)
+    }
+
+    public func updateBackup(data: Data) {
+        guard let directoryUrl = try? FileManager.default.url(
+            for: .applicationSupportDirectory,
+            in: .userDomainMask,
+            appropriateFor: nil,
+            create: true
+        ) else { fatalError("Couldn't generate the URL to persist the backup") }
+
+        let fileUrl = directoryUrl
+            .appendingPathComponent("backup")
+            .appendingPathExtension("xxm")
+
+        do {
+            try data.write(to: fileUrl)
+        } catch {
+            fatalError("Couldn't write backup to fileurl")
+        }
+
+        let isWifiOnly = settings.value.wifiOnlyBackup
+        let isAutomaticEnabled = settings.value.automaticBackups
+        let hasEnabledService = settings.value.enabledService != nil
+
+        if isWifiOnly {
+            guard connType == .wifi else { return }
+        } else {
+            guard connType != .unknown else { return }
+        }
+
+        if isAutomaticEnabled && hasEnabledService {
+            performBackup()
+        }
+    }
+
+    public func setBackupOnlyOnWifi(_ enabled: Bool) {
+        settings.value.wifiOnlyBackup = enabled
+    }
+
+    public func setBackupAutomatically(_ enabled: Bool) {
+        settings.value.automaticBackups = enabled
+
+        guard enabled else { return }
+        performBackup()
+    }
+
+    public func toggle(service: CloudService, enabling: Bool) {
+        settings.value.enabledService = enabling ? service : nil
+    }
+
+    public func authorize(service: CloudService, presenting screen: UIViewController) {
+        switch service {
+        case .drive:
+            driveService.authorize(presenting: screen) { [weak self] _ in
+                guard let self = self else { return }
+                self.refreshConnections()
+                self.refreshBackups()
+            }
+        case .icloud:
+            if !icloudService.isAuthorized() {
+                icloudService.openSettings()
+            } else {
+                refreshConnections()
+                refreshBackups()
+            }
+        case .dropbox:
+            if !dropboxService.isAuthorized() {
+                dropboxService.authorize(presenting: screen)
+                    .sink { [weak self] _ in
+                        guard let self = self else { return }
+                        self.refreshConnections()
+                        self.refreshBackups()
+                    }.store(in: &cancellables)
+            }
+        }
+    }
+}
+
+extension BackupService {
+    private func refreshConnections() {
+        if icloudService.isAuthorized() && !settings.value.connectedServices.contains(.icloud) {
+            settings.value.connectedServices.insert(.icloud)
+        } else if !icloudService.isAuthorized() && settings.value.connectedServices.contains(.icloud) {
+            settings.value.connectedServices.remove(.icloud)
+        }
+
+        if dropboxService.isAuthorized() && !settings.value.connectedServices.contains(.dropbox) {
+            settings.value.connectedServices.insert(.dropbox)
+        } else if !dropboxService.isAuthorized() && settings.value.connectedServices.contains(.dropbox) {
+            settings.value.connectedServices.remove(.dropbox)
+        }
+
+        driveService.isAuthorized { [weak settings] isAuthorized in
+            guard let settings = settings else { return }
+
+            if isAuthorized && !settings.value.connectedServices.contains(.drive) {
+                settings.value.connectedServices.insert(.drive)
+            } else if !isAuthorized && settings.value.connectedServices.contains(.drive) {
+                settings.value.connectedServices.remove(.drive)
+            }
+        }
+    }
+
+    private func refreshBackups() {
+        if icloudService.isAuthorized() {
+            icloudService.downloadMetadata { [weak settings] in
+                guard let settings = settings else { return }
+
+                guard let metadata = try? $0.get() else {
+                    settings.value.backups[.icloud] = nil
+                    return
+                }
+
+                settings.value.backups[.icloud] = Backup(
+                    id: metadata.path,
+                    date: metadata.modifiedDate,
+                    size: metadata.size
+                )
+            }
+        }
+
+        if dropboxService.isAuthorized() {
+            dropboxService.downloadMetadata { [weak settings] in
+                guard let settings = settings else { return }
+
+                guard let metadata = try? $0.get() else {
+                    settings.value.backups[.dropbox] = nil
+                    return
+                }
+
+                settings.value.backups[.dropbox] = Backup(
+                    id: metadata.path,
+                    date: metadata.modifiedDate,
+                    size: metadata.size
+                )
+            }
+        }
+
+        driveService.isAuthorized { [weak settings] isAuthorized  in
+            guard let settings = settings else { return }
+
+            if isAuthorized {
+                self.driveService.downloadMetadata {
+                    guard let metadata = try? $0.get() else { return }
+
+                    settings.value.backups[.drive] = Backup(
+                        id: metadata.identifier,
+                        date: metadata.modifiedDate,
+                        size: metadata.size
+                    )
+                }
+            } else {
+                settings.value.backups[.drive] = nil
+            }
+        }
+    }
+
+    private func performBackup(data: Data) {
+        guard let enabledService = settings.value.enabledService else {
+            fatalError("Trying to backup but nothing is enabled")
+        }
+
+        let url = URL(fileURLWithPath: NSTemporaryDirectory())
+            .appendingPathComponent(UUID().uuidString)
+
+        do {
+            try data.write(to: url)
+        } catch {
+            print("Couldn't write to temp: \(error.localizedDescription)")
+            return
+        }
+
+        switch enabledService {
+        case .drive:
+            driveService.uploadBackup(url) {
+                switch $0 {
+                case .success(let metadata):
+                    self.settings.value.backups[.drive] = .init(
+                        id: metadata.identifier,
+                        date: metadata.modifiedDate,
+                        size: metadata.size
+                    )
+                case .failure(let error):
+                    print(error.localizedDescription)
+                }
+
+                // try? FileManager.default.removeItem(at: url)
+            }
+        case .icloud:
+            icloudService.uploadBackup(url) {
+                switch $0 {
+                case .success(let metadata):
+                    self.settings.value.backups[.icloud] = .init(
+                        id: metadata.path,
+                        date: metadata.modifiedDate,
+                        size: metadata.size
+                    )
+                case .failure(let error):
+                    print(error.localizedDescription)
+                }
+
+                // try? FileManager.default.removeItem(at: url)
+            }
+        case .dropbox:
+            dropboxService.uploadBackup(url) {
+                switch $0 {
+                case .success(let metadata):
+                    self.settings.value.backups[.dropbox] = .init(
+                        id: metadata.path,
+                        date: metadata.modifiedDate,
+                        size: metadata.size
+                    )
+                case .failure(let error):
+                    print(error.localizedDescription)
+                }
+
+                // try? FileManager.default.removeItem(at: url)
+            }
+        }
+    }
+}
diff --git a/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift b/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c11b31a8183899a257d755270d9628c39c92d987
--- /dev/null
+++ b/Sources/BackupFeature/ViewModels/BackupConfigViewModel.swift
@@ -0,0 +1,87 @@
+import UIKit
+import Models
+import Shared
+import Combine
+import Foundation
+import DependencyInjection
+import HUD
+
+enum BackupActionState {
+    case backupFinished
+    case backupAllowed(Bool)
+    case backupInProgress(Float, Float)
+}
+
+struct BackupConfigViewModel {
+    var didTapBackupNow: () -> Void
+    var didChooseWifiOnly: (Bool) -> Void
+    var didChooseAutomatic: (Bool) -> Void
+    var didToggleService: (CloudService, Bool) -> Void
+    var didTapService: (CloudService, UIViewController) -> Void
+
+    var wifiOnly: () -> AnyPublisher<Bool, Never>
+    var automatic: () -> AnyPublisher<Bool, Never>
+    var lastBackup: () -> AnyPublisher<Backup?, Never>
+    var actionState: () -> AnyPublisher<BackupActionState, Never>
+    var enabledService: () -> AnyPublisher<CloudService?, Never>
+    var connectedServices: () -> AnyPublisher<Set<CloudService>, Never>
+}
+
+extension BackupConfigViewModel {
+    static func live() -> Self {
+        class Context {
+            @Dependency var hud: HUDType
+            @Dependency var service: BackupService
+        }
+
+        let context = Context()
+
+        return .init(
+            didTapBackupNow: {
+                context.service.performBackup()
+                context.hud.update(with: .on)
+                DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
+                    context.hud.update(with: .none)
+                }
+            },
+            didChooseWifiOnly: context.service.setBackupOnlyOnWifi(_:),
+            didChooseAutomatic: context.service.setBackupAutomatically(_:),
+            didToggleService: context.service.toggle,
+            didTapService: context.service.authorize,
+            wifiOnly: {
+                context.service.settingsPublisher
+                    .map(\.wifiOnlyBackup)
+                    .eraseToAnyPublisher()
+            },
+            automatic: {
+                context.service.settingsPublisher
+                    .map(\.automaticBackups)
+                    .eraseToAnyPublisher()
+            },
+            lastBackup: {
+                context.service.settingsPublisher
+                    .map {
+                        guard let enabledService = $0.enabledService else { return nil }
+                        return $0.backups[enabledService]
+                    }.eraseToAnyPublisher()
+            },
+            actionState: {
+                context.service.settingsPublisher
+                    .map(\.enabledService)
+                    .map { BackupActionState.backupAllowed($0 != nil) }
+                    .eraseToAnyPublisher()
+            },
+            enabledService: {
+                context.service.settingsPublisher
+                    .map(\.enabledService)
+                    .eraseToAnyPublisher()
+            },
+            connectedServices: {
+                context.service.settingsPublisher
+                    .map(\.connectedServices)
+                    .removeDuplicates()
+                    .eraseToAnyPublisher()
+            }
+        )
+    }
+}
diff --git a/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift b/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..cc647d9aaecc7507eed9d517909dbf3229b6901f
--- /dev/null
+++ b/Sources/BackupFeature/ViewModels/BackupSetupViewModel.swift
@@ -0,0 +1,21 @@
+import UIKit
+import Models
+import Shared
+import Combine
+import GoogleDriveFeature
+import DependencyInjection
+
+struct BackupSetupViewModel {
+    var didTapService: (CloudService, UIViewController) -> Void
+}
+
+extension BackupSetupViewModel {
+    static func live() -> Self {
+        class Context {
+            @Dependency var service: BackupService
+        }
+
+        let context = Context()
+        return .init(didTapService: context.service.authorize)
+    }
+}
diff --git a/Sources/BackupFeature/ViewModels/BackupViewModel.swift b/Sources/BackupFeature/ViewModels/BackupViewModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..6522ee1215a4ee84000c85600389bf53d52aa9bc
--- /dev/null
+++ b/Sources/BackupFeature/ViewModels/BackupViewModel.swift
@@ -0,0 +1,35 @@
+import Combine
+import DependencyInjection
+
+enum BackupViewState: Equatable {
+    case setup
+    case config
+}
+
+struct BackupViewModel {
+    var setupViewModel: () -> BackupSetupViewModel
+    var configViewModel: () -> BackupConfigViewModel
+
+    var state: () -> AnyPublisher<BackupViewState, Never>
+}
+
+extension BackupViewModel {
+    static func live() -> Self {
+        class Context {
+            @Dependency var service: BackupService
+        }
+
+        let context = Context()
+
+        return .init(
+            setupViewModel: { BackupSetupViewModel.live() },
+            configViewModel: { BackupConfigViewModel.live() },
+            state: {
+                context.service.settingsPublisher
+                    .map(\.connectedServices)
+                    .map { $0.isEmpty ? BackupViewState.setup : .config }
+                    .eraseToAnyPublisher()
+            }
+        )
+    }
+}
diff --git a/Sources/BackupFeature/Views/BackupActionView.swift b/Sources/BackupFeature/Views/BackupActionView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..705263e0ef815ae9fab0ff5657cc536f02c98101
--- /dev/null
+++ b/Sources/BackupFeature/Views/BackupActionView.swift
@@ -0,0 +1,125 @@
+import UIKit
+import Shared
+
+final class BackupActionView: UIView {
+    let stackView = UIStackView()
+    let backupNowButton = CapsuleButton()
+
+    let progressView = UIView()
+    let progressLabel = UILabel()
+    let progressBarPartial = UIView()
+    let progressBarFull = UIView()
+
+    let finishedView = UIView()
+    let finishedLabel = UILabel()
+    let finishedImage = UIImageView()
+
+    init() {
+        super.init(frame: .zero)
+
+        setupProgressView()
+        setupFinishedView()
+
+        backupNowButton.set(style: .brandColored, title: Localized.Backup.Config.backupNow)
+
+        stackView.spacing = 15
+        stackView.axis = .vertical
+        stackView.addArrangedSubview(backupNowButton)
+        stackView.addArrangedSubview(progressView)
+        stackView.addArrangedSubview(finishedView)
+
+        addSubview(stackView)
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    private func setupFinishedView() {
+        finishedImage.contentMode = .center
+        finishedImage.image = Asset.restoreSuccess.image
+
+        finishedLabel.text = "Backup completed!"
+        finishedLabel.textColor = Asset.neutralBody.color
+        finishedLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+
+        finishedView.addSubview(finishedImage)
+        finishedView.addSubview(finishedLabel)
+
+        finishedImage.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+
+        finishedLabel.snp.makeConstraints { make in
+            make.left.equalTo(finishedImage.snp.right).offset(10)
+            make.centerY.equalTo(finishedImage)
+            make.right.lessThanOrEqualToSuperview()
+        }
+    }
+
+    private func setupProgressView() {
+        progressLabel.textColor = Asset.neutralDisabled.color
+        progressLabel.font = Fonts.Mulish.regular.font(size: 14.0)
+
+        progressBarFull.backgroundColor = Asset.neutralLine.color
+        progressBarPartial.backgroundColor = Asset.brandPrimary.color
+        progressBarFull.layer.masksToBounds = true
+        progressBarFull.layer.cornerRadius = 4
+
+        progressBarFull.addSubview(progressBarPartial)
+        progressView.addSubview(progressLabel)
+        progressView.addSubview(progressBarFull)
+
+        progressBarFull.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+            make.height.equalTo(8)
+        }
+
+        progressLabel.snp.makeConstraints { make in
+            make.top.equalTo(progressBarFull.snp.bottom).offset(10)
+            make.left.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+
+        progressBarPartial.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+            make.width.equalToSuperview().multipliedBy(0.5)
+            make.bottom.equalToSuperview()
+        }
+    }
+
+    func setState(_ state: BackupActionState) {
+        switch state {
+        case .backupFinished:
+            backupNowButton.isHidden = true
+            progressView.isHidden = true
+            finishedView.isHidden = false
+
+        case .backupAllowed(let bool):
+            backupNowButton.isHidden = false
+            progressView.isHidden = true
+            finishedView.isHidden = true
+            backupNowButton.isEnabled = bool
+
+        case .backupInProgress(let uploaded, let total):
+            backupNowButton.isHidden = true
+            progressView.isHidden = false
+            finishedView.isHidden = true
+
+            let uploadedKb = String(format: "%.1f kb", uploaded/1000)
+            let totalkb = String(format: "%.1f kb", total/1000)
+
+            progressLabel.text = "Uploaded \(uploadedKb) of \(totalkb) (\(total/uploaded)%)"
+        }
+    }
+}
diff --git a/Sources/BackupFeature/Views/BackupConfigView.swift b/Sources/BackupFeature/Views/BackupConfigView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8c400b3ff9162f380548d0478f2356454910833e
--- /dev/null
+++ b/Sources/BackupFeature/Views/BackupConfigView.swift
@@ -0,0 +1,112 @@
+import UIKit
+import Shared
+
+final class BackupConfigView: UIView {
+    let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
+    let actionView = BackupActionView()
+
+    let stackView = UIStackView()
+    let iCloudButton = BackupSwitcherButton()
+    let dropboxButton = BackupSwitcherButton()
+    let googleDriveButton = BackupSwitcherButton()
+
+    let enabledSubtitleView = UIView()
+    let enabledSubtitleLabel = UILabel()
+    let frequencyDetailView = BackupDetailView()
+    let latestBackupDetailView = BackupDetailView()
+    let infrastructureDetailView = BackupDetailView()
+
+    init() {
+        super.init(frame: .zero)
+        backgroundColor = Asset.neutralWhite.color
+
+        titleLabel.textColor = Asset.neutralDark.color
+        titleLabel.text = Localized.Backup.Config.title
+        titleLabel.font = Fonts.Mulish.bold.font(size: 24.0)
+
+        enabledSubtitleLabel.numberOfLines = 0
+        enabledSubtitleLabel.textColor = Asset.neutralWeak.color
+        enabledSubtitleLabel.font = Fonts.Mulish.regular.font(size: 14.0)
+
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineHeightMultiple = 1.15
+
+        let attString = NSAttributedString(
+            string: Localized.Backup.subtitle,
+            attributes: [
+                .foregroundColor: Asset.neutralBody.color,
+                .font: Fonts.Mulish.regular.font(size: 16.0) as Any,
+                .paragraphStyle: paragraph
+            ])
+
+        subtitleLabel.numberOfLines = 0
+        subtitleLabel.attributedText = attString
+
+        iCloudButton.titleLabel.text = Localized.Backup.iCloud
+        iCloudButton.logoImageView.image = Asset.restoreIcloud.image
+
+        dropboxButton.titleLabel.text = Localized.Backup.dropbox
+        dropboxButton.logoImageView.image = Asset.restoreDropbox.image
+
+        googleDriveButton.titleLabel.text = Localized.Backup.googleDrive
+        googleDriveButton.logoImageView.image = Asset.restoreDrive.image
+
+        latestBackupDetailView.titleLabel.text = Localized.Backup.Config.latestBackup
+        frequencyDetailView.accessoryImageView.image = Asset.settingsDisclosure.image
+
+        infrastructureDetailView.titleLabel.text = Localized.Backup.Config.infrastructure.uppercased()
+        infrastructureDetailView.accessoryImageView.image = Asset.settingsDisclosure.image
+
+        enabledSubtitleView.addSubview(enabledSubtitleLabel)
+
+        stackView.axis = .vertical
+        stackView.addArrangedSubview(googleDriveButton)
+        stackView.addArrangedSubview(iCloudButton)
+        stackView.addArrangedSubview(dropboxButton)
+        stackView.addArrangedSubview(enabledSubtitleView)
+        stackView.addArrangedSubview(latestBackupDetailView)
+        stackView.addArrangedSubview(frequencyDetailView)
+        stackView.addArrangedSubview(infrastructureDetailView)
+
+        addSubview(titleLabel)
+        addSubview(subtitleLabel)
+        addSubview(actionView)
+        addSubview(stackView)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(15)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        enabledSubtitleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(-10)
+            make.left.equalToSuperview().offset(92)
+            make.right.equalToSuperview().offset(-48)
+            make.bottom.equalToSuperview()
+        }
+
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(8)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        actionView.snp.makeConstraints { make in
+            make.top.equalTo(subtitleLabel.snp.bottom).offset(15)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-38)
+        }
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalTo(actionView.snp.bottom).offset(28)
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+            make.bottom.lessThanOrEqualToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/BackupFeature/Views/BackupDetailView.swift b/Sources/BackupFeature/Views/BackupDetailView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..d28647330d7cd7b81d516ad7d4b506f712d94d0c
--- /dev/null
+++ b/Sources/BackupFeature/Views/BackupDetailView.swift
@@ -0,0 +1,40 @@
+import UIKit
+import Shared
+
+final class BackupDetailView: UIControl {
+    let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
+    let accessoryImageView = UIImageView()
+
+    init() {
+        super.init(frame: .zero)
+
+        titleLabel.font = Fonts.Mulish.bold.font(size: 12.0)
+        subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+
+        titleLabel.textColor = Asset.neutralWeak.color
+        subtitleLabel.textColor = Asset.neutralActive.color
+
+        addSubview(titleLabel)
+        addSubview(subtitleLabel)
+        addSubview(accessoryImageView)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(20)
+            make.left.equalToSuperview().offset(92)
+        }
+
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(4)
+            make.left.equalTo(titleLabel)
+            make.bottom.equalToSuperview().offset(-2)
+        }
+
+        accessoryImageView.snp.makeConstraints { make in
+            make.right.equalToSuperview().offset(-48)
+            make.centerY.equalTo(titleLabel.snp.bottom)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/BackupFeature/Views/BackupSetupView.swift b/Sources/BackupFeature/Views/BackupSetupView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..eeaad48195da765ac9833293ccd7f7f9a7673ca0
--- /dev/null
+++ b/Sources/BackupFeature/Views/BackupSetupView.swift
@@ -0,0 +1,93 @@
+import UIKit
+import Shared
+
+final class BackupSetupView: UIView {
+    let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
+
+    let stackView = UIStackView()
+    let iCloudButton = BackupSwitcherButton()
+    let dropboxButton = BackupSwitcherButton()
+    let googleDriveButton = BackupSwitcherButton()
+
+    init() {
+        super.init(frame: .zero)
+        backgroundColor = Asset.neutralWhite.color
+
+        let title = Localized.Backup.Setup.title
+
+        let attString = NSMutableAttributedString(string: title)
+        let firstParagraph = NSMutableParagraphStyle()
+        firstParagraph.alignment = .left
+        firstParagraph.lineHeightMultiple = 1
+
+        attString.addAttribute(.paragraphStyle, value: firstParagraph)
+        attString.addAttribute(.foregroundColor, value: Asset.neutralActive.color)
+        attString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 34.0) as Any)
+
+        attString.addAttributes(attributes: [
+            .font: Fonts.Mulish.bold.font(size: 34.0) as Any,
+            .foregroundColor: Asset.brandPrimary.color
+        ], betweenCharacters: "#")
+
+        titleLabel.numberOfLines = 0
+        titleLabel.attributedText = attString
+
+        let secondParagraph = NSMutableParagraphStyle()
+        secondParagraph.alignment = .left
+        secondParagraph.lineHeightMultiple = 1.15
+
+        let secondAttString = NSAttributedString(
+            string: Localized.Backup.subtitle,
+            attributes: [
+                .foregroundColor: Asset.neutralBody.color,
+                .font: Fonts.Mulish.regular.font(size: 16.0) as Any,
+                .paragraphStyle: secondParagraph
+            ])
+
+        subtitleLabel.numberOfLines = 0
+        subtitleLabel.attributedText = secondAttString
+
+        iCloudButton.titleLabel.text = Localized.Backup.iCloud
+        iCloudButton.logoImageView.image = Asset.restoreIcloud.image
+        iCloudButton.showChevron()
+
+        dropboxButton.titleLabel.text = Localized.Backup.dropbox
+        dropboxButton.logoImageView.image = Asset.restoreDropbox.image
+        dropboxButton.showChevron()
+
+        googleDriveButton.titleLabel.text = Localized.Backup.googleDrive
+        googleDriveButton.logoImageView.image = Asset.restoreDrive.image
+        googleDriveButton.showChevron()
+
+        stackView.axis = .vertical
+        stackView.addArrangedSubview(googleDriveButton)
+        stackView.addArrangedSubview(iCloudButton)
+        stackView.addArrangedSubview(dropboxButton)
+
+        addSubview(titleLabel)
+        addSubview(subtitleLabel)
+        addSubview(stackView)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(15)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(8)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalTo(subtitleLabel.snp.bottom).offset(28)
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+            make.bottom.lessThanOrEqualToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/BackupFeature/Views/BackupSwitcherButton.swift b/Sources/BackupFeature/Views/BackupSwitcherButton.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2c115cdc9b723cc7c2ece188e17ef5e3a7ea7533
--- /dev/null
+++ b/Sources/BackupFeature/Views/BackupSwitcherButton.swift
@@ -0,0 +1,69 @@
+import UIKit
+import Shared
+
+final class BackupSwitcherButton: UIControl {
+    let titleLabel = UILabel()
+    let separatorView = UIView()
+    let switcherView = UISwitch()
+    let logoImageView = UIImageView()
+    let chevronImageView = UIImageView()
+
+    init() {
+        super.init(frame: .zero)
+
+        titleLabel.textColor = Asset.neutralActive.color
+        titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0)
+
+        switcherView.onTintColor = Asset.brandLight.color
+        chevronImageView.image = Asset.settingsDisclosure.image
+        separatorView.backgroundColor = Asset.neutralLine.color
+
+        addSubview(separatorView)
+        addSubview(logoImageView)
+        addSubview(titleLabel)
+        addSubview(switcherView)
+        addSubview(chevronImageView)
+
+        logoImageView.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(20)
+            make.left.equalToSuperview().offset(36)
+            make.bottom.equalToSuperview().offset(-20)
+        }
+
+        titleLabel.snp.makeConstraints { make in
+            make.left.equalTo(logoImageView.snp.right).offset(15)
+            make.centerY.equalTo(logoImageView)
+        }
+
+        chevronImageView.snp.makeConstraints { make in
+            make.centerY.equalTo(logoImageView)
+            make.right.equalToSuperview().offset(-48)
+        }
+
+        switcherView.snp.makeConstraints { make in
+            make.right.equalToSuperview().offset(-25)
+            make.centerY.equalTo(logoImageView)
+        }
+
+        separatorView.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.height.equalTo(1)
+            make.left.equalToSuperview().offset(24)
+            make.right.equalToSuperview().offset(-24)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    func showSwitcher(enabled: Bool) {
+        switcherView.isOn = enabled
+        switcherView.isHidden = false
+        chevronImageView.isHidden = true
+    }
+
+    func showChevron() {
+        switcherView.isOn = false
+        switcherView.isHidden = true
+        chevronImageView.isHidden = false
+    }
+}
diff --git a/Sources/ChatFeature/Coordinator/ChatCoordinator.swift b/Sources/ChatFeature/Coordinator/ChatCoordinator.swift
index 451a3b579d0bcb14477ac1afc0a4c9a428e69e4f..ce1f35c8e71789508d88cca0294a03a934801804 100644
--- a/Sources/ChatFeature/Coordinator/ChatCoordinator.swift
+++ b/Sources/ChatFeature/Coordinator/ChatCoordinator.swift
@@ -2,118 +2,104 @@ import UIKit
 import Models
 import Shared
 import QuickLook
-import Presentation
 import Permissions
+import Presentation
 
 public protocol ChatCoordinating {
     func toCamera(from: UIViewController)
     func toLibrary(from: UIViewController)
     func toPreview(from: UIViewController)
-    func toPermission(type: PermissionType, from: UIViewController)
-    func toWebview(with: String, from: UIViewController)
-
     func toRetrySheet(from: UIViewController)
     func toContact(_: Contact, from: UIViewController)
+    func toWebview(with: String, from: UIViewController)
     func toPopup(_: UIViewController, from: UIViewController)
     func toMenuSheet(_: UIViewController, from: UIViewController)
+    func toPermission(type: PermissionType, from: UIViewController)
     func toMembersList(_: UIViewController, from: UIViewController)
 }
 
 public struct ChatCoordinator: ChatCoordinating {
+    var pushPresenter: Presenting = PushPresenter()
+    var modalPresenter: Presenting = ModalPresenter()
+    var bottomPresenter: Presenting = BottomPresenter()
+
+    var retryFactory: () -> UIViewController
+    var webFactory: (String) -> UIViewController
+    var previewFactory: () -> QLPreviewController
+    var contactFactory: (Contact) -> UIViewController
+    var imagePickerFactory: () -> UIImagePickerController
+    var permissionFactory: () -> RequestPermissionController
+
     public init(
         retryFactory: @escaping () -> UIViewController,
-        contactFactory: @escaping (Contact) -> UIViewController
+        webFactory: @escaping (String) -> UIViewController,
+        previewFactory: @escaping () -> QLPreviewController,
+        contactFactory: @escaping (Contact) -> UIViewController,
+        imagePickerFactory: @escaping () -> UIImagePickerController,
+        permissionFactory: @escaping () -> RequestPermissionController
     ) {
+        self.webFactory = webFactory
         self.retryFactory = retryFactory
+        self.previewFactory = previewFactory
         self.contactFactory = contactFactory
+        self.permissionFactory = permissionFactory
+        self.imagePickerFactory = imagePickerFactory
     }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
-    var presenter: Presenting = ModalPresenter()
-    var bottomPresenter: Presenting = BottomPresenter()
-
-    // MARK: Factories
-
-    var webFactory: (String) -> UIViewController = WebScreen.init(url:)
-
-    var retryFactory: () -> UIViewController
-    var contactFactory: (Contact) -> UIViewController
-
-    var previewFactory: () -> QLPreviewController = QLPreviewController.init
-    var permissionFactory: () -> RequestPermissionController = RequestPermissionController.init
-    var imagePickerFactory: () -> UIImagePickerController = UIImagePickerController.init
 }
 
 public extension ChatCoordinator {
-    func toWebview(
-        with urlString: String,
-        from parent: UIViewController
-    ) {
-        let screen = webFactory(urlString)
-        presenter.present(screen, from: parent)
+    func toPreview(from parent: UIViewController) {
+        let screen = previewFactory()
+        screen.delegate = (parent as? QLPreviewControllerDelegate)
+        screen.dataSource = (parent as? QLPreviewControllerDataSource)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toPermission(type: PermissionType, from parent: UIViewController) {
-        let screen = permissionFactory()
-        screen.setup(type: type)
-        pusher.present(screen, from: parent)
+    func toLibrary(from parent: UIViewController) {
+        let screen = imagePickerFactory()
+        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
+        screen.allowsEditing = false
+        modalPresenter.present(screen, from: parent)
     }
 
-    func toMembersList(
-        _ screen: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(screen, from: parent)
+    func toCamera(from parent: UIViewController) {
+        let screen = imagePickerFactory()
+        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
+        screen.sourceType = .camera
+        screen.allowsEditing = false
+        modalPresenter.present(screen, from: parent)
     }
 
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(popup, from: parent)
+    func toRetrySheet(from parent: UIViewController) {
+        let screen = retryFactory()
+        bottomPresenter.present(screen, from: parent)
     }
 
-    func toContact(
-        _ contact: Contact,
-        from parent: UIViewController
-    ) {
+    func toContact(_ contact: Contact, from parent: UIViewController) {
         let screen = contactFactory(contact)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toMenuSheet(
-        _ screen: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(screen, from: parent)
+    func toWebview(with urlString: String, from parent: UIViewController) {
+        let screen = webFactory(urlString)
+        modalPresenter.present(screen, from: parent)
     }
 
-    func toRetrySheet(from parent: UIViewController) {
-        let screen = retryFactory()
-        bottomPresenter.present(screen, from: parent)
+    func toPermission(type: PermissionType, from parent: UIViewController) {
+        let screen = permissionFactory()
+        screen.setup(type: type)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toLibrary(from parent: UIViewController) {
-        let screen = imagePickerFactory()
-        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
-        screen.allowsEditing = false
-        presenter.present(screen, from: parent)
+    func toMembersList(_ screen: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(screen, from: parent)
     }
 
-    func toCamera(from parent: UIViewController) {
-        let screen = imagePickerFactory()
-        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
-        screen.sourceType = .camera
-        screen.allowsEditing = false
-        presenter.present(screen, from: parent)
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
     }
 
-    func toPreview(from parent: UIViewController) {
-        let screen = previewFactory()
-        screen.delegate = (parent as? QLPreviewControllerDelegate)
-        screen.dataSource = (parent as? QLPreviewControllerDataSource)
-        pusher.present(screen, from: parent)
+    func toMenuSheet(_ screen: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(screen, from: parent)
     }
 }
diff --git a/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift b/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
index 5a7a40bcc95a3aa31043027e2d827457411e1d53..f1235d08ac6038666523b98ebe2c48ea6822d4be 100644
--- a/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
+++ b/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
@@ -14,25 +14,38 @@ public protocol ChatListCoordinating {
     func toSettings(from: UIViewController)
     func toContacts(from: UIViewController)
     func toRequests(from: UIViewController)
+    func toSingleChat(with: Contact, from: UIViewController)
     func toPopup(_: UIViewController, from: UIViewController)
+    func toGroupChat(with: GroupChatInfo, from: UIViewController)
     func toSideMenu<T: UIViewController>(from: T) where T: MenuDelegate
-
-    func toSingleChat(with: Contact, from: UIViewController)
-
-    func toGroupChat(
-        with: GroupChatInfo,
-        from: UIViewController
-    )
 }
 
 public struct ChatListCoordinator: ChatListCoordinating {
+    var pushPresenter: Presenting = PushPresenter()
+    var modalPresenter: Presenting = ModalPresenter()
+    var sidePresenter: Presenting = SideMenuPresenter()
+    var bottomPresenter: Presenting = BottomPresenter()
+
+    var scanFactory: () -> UIViewController
+    var searchFactory: () -> UIViewController
+    var profileFactory: () -> UIViewController
+    var settingsFactory: () -> UIViewController
+    var contactsFactory: () -> UIViewController
+    var requestsFactory: () -> UIViewController
+    var singleChatFactory: (Contact) -> UIViewController
+    var sideMenuFactory: (MenuDelegate) -> UIViewController
+    var groupChatFactory: (GroupChatInfo) -> UIViewController
+
     public init(
         scanFactory: @escaping () -> UIViewController,
         searchFactory: @escaping () -> UIViewController,
         profileFactory: @escaping () -> UIViewController,
         settingsFactory: @escaping () -> UIViewController,
         contactsFactory: @escaping () -> UIViewController,
-        requestsFactory: @escaping () -> UIViewController
+        requestsFactory: @escaping () -> UIViewController,
+        singleChatFactory: @escaping (Contact) -> UIViewController,
+        sideMenuFactory: @escaping (MenuDelegate) -> UIViewController,
+        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController
     ) {
         self.scanFactory = scanFactory
         self.searchFactory = searchFactory
@@ -40,90 +53,59 @@ public struct ChatListCoordinator: ChatListCoordinating {
         self.settingsFactory = settingsFactory
         self.contactsFactory = contactsFactory
         self.requestsFactory = requestsFactory
+        self.sideMenuFactory = sideMenuFactory
+        self.groupChatFactory = groupChatFactory
+        self.singleChatFactory = singleChatFactory
     }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
-    var sider: Presenting = SideMenuPresenter()
-    var presenter: Presenting = ModalPresenter()
-    var bottomPresenter: Presenting = BottomPresenter()
-
-    // MARK: Factories
-
-    var scanFactory: () -> UIViewController
-    var searchFactory: () -> UIViewController
-    var profileFactory: () -> UIViewController
-    var settingsFactory: () -> UIViewController
-    var contactsFactory: () -> UIViewController
-    var requestsFactory: () -> UIViewController
-
-    var groupChatFactory: (GroupChatInfo) -> UIViewController
-    = GroupChatController.init(_:)
-
-    var singleChatFactory: (Contact) -> UIViewController
-    = SingleChatController.init(_:)
-
-    var sideMenuFactory: (MenuDelegate) -> UIViewController
-    = MenuController.init(_:)
 }
 
 public extension ChatListCoordinator {
-    func toSingleChat(
-        with contact: Contact,
-        from parent: UIViewController
-    ) {
-        let screen = singleChatFactory(contact)
-        pusher.present(screen, from: parent)
-    }
-
-    func toGroupChat(
-        with group: GroupChatInfo,
-        from parent: UIViewController
-    ) {
-        let screen = groupChatFactory(group)
-        pusher.present(screen, from: parent)
-    }
-
-    func toSideMenu<T: UIViewController>(from parent: T) where T: MenuDelegate {
-        let screen = sideMenuFactory(parent)
-        sider.present(screen, from: parent)
-    }
-
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(popup, from: parent)
-    }
-
     func toSearch(from parent: UIViewController) {
         let screen = searchFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toScan(from parent: UIViewController) {
         let screen = scanFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toProfile(from parent: UIViewController) {
         let screen = profileFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toContacts(from parent: UIViewController) {
         let screen = contactsFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toSettings(from parent: UIViewController) {
         let screen = settingsFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toRequests(from parent: UIViewController) {
         let screen = requestsFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toSingleChat(with contact: Contact, from parent: UIViewController) {
+        let screen = singleChatFactory(contact)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toGroupChat(with group: GroupChatInfo, from parent: UIViewController) {
+        let screen = groupChatFactory(group)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toSideMenu<T: UIViewController>(from parent: T) where T: MenuDelegate {
+        let screen = sideMenuFactory(parent)
+        sidePresenter.present(screen, from: parent)
+    }
+
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
     }
 }
diff --git a/Sources/ContactFeature/Controllers/ContactController.swift b/Sources/ContactFeature/Controllers/ContactController.swift
index b19b44c380799c3015a705dbdbfd2968ea06a3bf..5b6abe47459e937c6cf52b992a1e5cf01882dcfb 100644
--- a/Sources/ContactFeature/Controllers/ContactController.swift
+++ b/Sources/ContactFeature/Controllers/ContactController.swift
@@ -261,7 +261,7 @@ public final class ContactController: UIViewController {
                 screenView.confirmedView.stackView.addArrangedSubview(phoneAttribute)
 
                 let deleteButton = RowButton()
-                deleteButton.set(
+                deleteButton.setup(
                     title: "Delete Connection",
                     icon: Asset.settingsDelete.image,
                     style: .delete,
diff --git a/Sources/ContactFeature/Controllers/NickameController.swift b/Sources/ContactFeature/Controllers/NickameController.swift
index 92f59bc1e31d096cb6a89b4a8db2038e4e0c6152..c3f00b42f9a05a2fd289d7a241787f7df9c95daf 100644
--- a/Sources/ContactFeature/Controllers/NickameController.swift
+++ b/Sources/ContactFeature/Controllers/NickameController.swift
@@ -5,21 +5,15 @@ import InputField
 import ScrollViewController
 
 public final class NickameController: UIViewController {
-    // MARK: UI
-
     lazy private var screenView = NickameView()
 
-    // MARK: Properties
-
     private let prefilled: String
     private let completion: StringClosure
     private let viewModel = NicknameViewModel()
     private var cancellables = Set<AnyCancellable>()
     private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default)
 
-    // MARK: Lifecycle
-
-    public init(prefilled: String, _ completion: @escaping StringClosure) {
+    public init(_ prefilled: String, _ completion: @escaping StringClosure) {
         self.prefilled = prefilled
         self.completion = completion
         super.init(nibName: nil, bundle: nil)
@@ -50,8 +44,6 @@ public final class NickameController: UIViewController {
         viewModel.didInput(prefilled)
     }
 
-    // MARK: Private
-
     private func setupKeyboard() {
         keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in
             guard let self = self else { return }
diff --git a/Sources/ContactFeature/Coordinator/ContactCoordinator.swift b/Sources/ContactFeature/Coordinator/ContactCoordinator.swift
index cf8e67dcb0763550e23495544a310486549fc303..a9ceab13dd5e0614b37bc3e8d05223d4ff9a09bd 100644
--- a/Sources/ContactFeature/Coordinator/ContactCoordinator.swift
+++ b/Sources/ContactFeature/Coordinator/ContactCoordinator.swift
@@ -13,57 +13,35 @@ public protocol ContactCoordinating: AnyObject {
 }
 
 public final class ContactCoordinator: ContactCoordinating {
-    public init(requestsFactory: @escaping () -> UIViewController) {
-        self.requestsFactory = requestsFactory
-    }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
-    var presenter: Presenting = ModalPresenter()
+    var pushPresenter: Presenting = PushPresenter()
+    var modalPresenter: Presenting = ModalPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
-    var replacer: Presenting = ReplacePresenter(mode: .replaceBackwards(SingleChatController.self))
-
-    // MARK: Factories
+    var replacePresenter: Presenting = ReplacePresenter(mode: .replaceBackwards(SingleChatController.self))
 
     var requestsFactory: () -> UIViewController
-
     var singleChatFactory: (Contact) -> UIViewController
-    = SingleChatController.init(_:)
-
     var imagePickerFactory: () -> UIImagePickerController
-    = UIImagePickerController.init
-
     var nicknameFactory: (String, @escaping StringClosure) -> UIViewController
-    = NickameController.init(prefilled:_:)
-}
-
-public extension ContactCoordinator {
-    func toRequests(from parent: UIViewController) {
-        let screen = requestsFactory()
-        pusher.present(screen, from: parent)
-    }
-
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(popup, from: parent)
-    }
 
-    func toSingleChat(
-        with contact: Contact,
-        from parent: UIViewController
+    public init(
+        requestsFactory: @escaping () -> UIViewController,
+        singleChatFactory: @escaping (Contact) -> UIViewController,
+        imagePickerFactory: @escaping () -> UIImagePickerController,
+        nicknameFactory: @escaping (String, @escaping StringClosure) -> UIViewController
     ) {
-        let screen = singleChatFactory(contact)
-        replacer.present(screen, from: parent)
+        self.requestsFactory = requestsFactory
+        self.singleChatFactory = singleChatFactory
+        self.imagePickerFactory = imagePickerFactory
+        self.nicknameFactory = nicknameFactory
     }
+}
 
+public extension ContactCoordinator {
     func toPhotos(from parent: UIViewController) {
         let screen = imagePickerFactory()
         screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
         screen.allowsEditing = true
-        presenter.present(screen, from: parent)
+        modalPresenter.present(screen, from: parent)
     }
 
     func toNickname(
@@ -74,4 +52,18 @@ public extension ContactCoordinator {
         let screen = nicknameFactory(prefilled, completion)
         bottomPresenter.present(screen, from: parent)
     }
+
+    func toRequests(from parent: UIViewController) {
+        let screen = requestsFactory()
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
+    }
+
+    func toSingleChat(with contact: Contact, from parent: UIViewController) {
+        let screen = singleChatFactory(contact)
+        replacePresenter.present(screen, from: parent)
+    }
 }
diff --git a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
index f3857bd8c7b7b31dab5de00aa92948b97273843e..99440fc5e403a2363639c7bd200309d0f194efc5 100644
--- a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
+++ b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
@@ -12,97 +12,81 @@ public protocol ContactListCoordinating {
     func toRequests(from: UIViewController)
     func toNewGroup(from: UIViewController)
     func toContact(_: Contact, from: UIViewController)
-
-    func toGroupChat(
-        with: GroupChatInfo,
-        from: UIViewController
-    )
-
-    func toGroupPopup(
-        with: Int,
-        from: UIViewController,
-        _: @escaping (String, String?) -> Void
-    )
+    func toGroupChat(with: GroupChatInfo, from: UIViewController)
+    func toGroupPopup(with: Int, from: UIViewController, _: @escaping (String, String?) -> Void)
 }
 
 public struct ContactListCoordinator: ContactListCoordinating {
-    public init(
-        scanFactory: @escaping () -> UIViewController,
-        searchFactory: @escaping () -> UIViewController,
-        newGroupFactory: @escaping () -> UIViewController,
-        requestsFactory: @escaping () -> UIViewController
-    ) {
-        self.scanFactory = scanFactory
-        self.searchFactory = searchFactory
-        self.newGroupFactory = newGroupFactory
-        self.requestsFactory = requestsFactory
-    }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
+    var pushPresenter: Presenting = PushPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
     var fullscreenPresenter: Presenting = FullscreenPresenter()
-    var replacer: Presenting = ReplacePresenter(mode: .replaceLast)
-
-    // MARK: Factories
+    var replacePresenter: Presenting = ReplacePresenter(mode: .replaceLast)
 
     var scanFactory: () -> UIViewController
     var searchFactory: () -> UIViewController
     var newGroupFactory: () -> UIViewController
     var requestsFactory: () -> UIViewController
-
     var contactFactory: (Contact) -> UIViewController
-    = ContactController.init(_:)
-
     var groupChatFactory: (GroupChatInfo) -> UIViewController
-    = GroupChatController.init(_:)
-
     var groupPopupFactory: (Int, @escaping (String, String?) -> Void) -> UIViewController
-    = CreatePopupController.init(_:_:)
+
+    public init(
+        scanFactory: @escaping () -> UIViewController,
+        searchFactory: @escaping () -> UIViewController,
+        newGroupFactory: @escaping () -> UIViewController,
+        requestsFactory: @escaping () -> UIViewController,
+        contactFactory: @escaping (Contact) -> UIViewController,
+        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController,
+        groupPopupFactory: @escaping (Int, @escaping (String, String?) -> Void) -> UIViewController
+    ) {
+        self.scanFactory = scanFactory
+        self.searchFactory = searchFactory
+        self.newGroupFactory = newGroupFactory
+        self.requestsFactory = requestsFactory
+        self.contactFactory = contactFactory
+        self.groupChatFactory = groupChatFactory
+        self.groupPopupFactory = groupPopupFactory
+    }
 }
 
 public extension ContactListCoordinator {
-    func toRequests(from parent: UIViewController) {
-        let screen = requestsFactory()
-        pusher.present(screen, from: parent)
+    func toGroupPopup(
+        with count: Int,
+        from parent: UIViewController,
+        _ completion: @escaping (String, String?) -> Void
+    ) {
+        let screen =  ScrollViewController.embedding(groupPopupFactory(count, completion))
+        fullscreenPresenter.present(screen, from: parent)
     }
 
     func toScan(from parent: UIViewController) {
         let screen = scanFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toSearch(from parent: UIViewController) {
         let screen = searchFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toContact(_ contact: Contact, from parent: UIViewController) {
-        let screen = contactFactory(contact)
-        pusher.present(screen, from: parent)
+    func toRequests(from parent: UIViewController) {
+        let screen = requestsFactory()
+        pushPresenter.present(screen, from: parent)
     }
 
     func toNewGroup(from parent: UIViewController) {
         let screen = newGroupFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toGroupChat(
-        with info: GroupChatInfo,
-        from parent: UIViewController
-    ) {
-        let screen = groupChatFactory(info)
-        replacer.present(screen, from: parent)
+    func toContact(_ contact: Contact, from parent: UIViewController) {
+        let screen = contactFactory(contact)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toGroupPopup(
-        with count: Int,
-        from parent: UIViewController,
-        _ completion: @escaping (String, String?) -> Void
-    ) {
-        let screen =  ScrollViewController.embedding(groupPopupFactory(count, completion))
-        fullscreenPresenter.present(screen, from: parent)
+    func toGroupChat(with info: GroupChatInfo, from parent: UIViewController) {
+        let screen = groupChatFactory(info)
+        replacePresenter.present(screen, from: parent)
     }
 }
 
diff --git a/Sources/Countries/CountryListController.swift b/Sources/Countries/CountryListController.swift
index 6f9377f343f70797e4a8d0b6dd92c9ae80ffec40..c0a8341b12754f055b27da96c01649395d581d3c 100644
--- a/Sources/Countries/CountryListController.swift
+++ b/Sources/Countries/CountryListController.swift
@@ -17,13 +17,13 @@ public final class CountryListController: UIViewController {
     private var cancellables = Set<AnyCancellable>()
     private var dataSource: UITableViewDiffableDataSource<SectionId, Country>!
 
-    public convenience init(_ didChoose: @escaping (Country) -> Void) {
-        logger.log("init()")
-
-        self.init()
+    public init(_ didChoose: @escaping (Country) -> Void) {
         self.didChoose = didChoose
+        super.init(nibName: nil, bundle: nil)
     }
 
+    required init?(coder: NSCoder) { nil }
+
     public override func viewWillAppear(_ animated: Bool) {
         logger.log("viewWillAppear()")
 
diff --git a/Sources/Defaults/KeyObject.swift b/Sources/Defaults/KeyObject.swift
index 22da5e88eeab1e15d6dc5886d10689db30352d7b..385c4992b1f35e76d9232822884d3570f125e51a 100644
--- a/Sources/Defaults/KeyObject.swift
+++ b/Sources/Defaults/KeyObject.swift
@@ -22,6 +22,10 @@ public enum Key: String {
 
     case theme
 
+    // MARK: Backup
+
+    case backupSettings
+
     // MARK: Settings
 
     case biometrics
@@ -29,7 +33,6 @@ public enum Key: String {
     case recordingLogs
     case crashReporting
     case icognitoKeyboard
-    case openedSettingsFirstTime
 
     case dummyTrafficOn
     case askedDummyTrafficOnce
diff --git a/Sources/DropboxFeature/DropboxInterface.swift b/Sources/DropboxFeature/DropboxInterface.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1a5aa02a054ad29816530447c3ed5d725573160b
--- /dev/null
+++ b/Sources/DropboxFeature/DropboxInterface.swift
@@ -0,0 +1,18 @@
+import UIKit
+import Combine
+
+public protocol DropboxInterface {
+    func isAuthorized() -> Bool
+
+    func unlink()
+
+    func handleOpenUrl(_ url: URL) -> Bool
+
+    func downloadBackup(_: String, _: @escaping (Result<Data, Error>) -> Void)
+
+    func uploadBackup(_: URL, _: @escaping (Result<DropboxMetadata, Error>) -> Void)
+
+    func downloadMetadata(_: @escaping (Result<DropboxMetadata?, Error>) -> Void)
+
+    func authorize(presenting: UIViewController) -> AnyPublisher<Result<Bool, Error>, Never>
+}
diff --git a/Sources/DropboxFeature/DropboxMetadata.swift b/Sources/DropboxFeature/DropboxMetadata.swift
new file mode 100644
index 0000000000000000000000000000000000000000..ceb4fe195242bf13ffb4e2a65a48d58aea282756
--- /dev/null
+++ b/Sources/DropboxFeature/DropboxMetadata.swift
@@ -0,0 +1,18 @@
+import Foundation
+import SwiftyDropbox
+
+public struct DropboxMetadata: Equatable {
+    public var size: Float
+    public var path: String
+    public var modifiedDate: Date
+
+    public init(
+        size: Float,
+        path: String,
+        modifiedDate: Date
+    ) {
+        self.size = size
+        self.path = path
+        self.modifiedDate = modifiedDate
+    }
+}
diff --git a/Sources/DropboxFeature/DropboxService.swift b/Sources/DropboxFeature/DropboxService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..e90ef79721443c67ae452e1a0105ab0b86c2e6ca
--- /dev/null
+++ b/Sources/DropboxFeature/DropboxService.swift
@@ -0,0 +1,209 @@
+import UIKit
+import Combine
+import SwiftyDropbox
+
+public struct DropboxService: DropboxInterface {
+    private let didAuthorizeSubject = PassthroughSubject<Result<Bool, Error>, Never>()
+
+    public init() {
+        let path = Bundle.module.path(forResource: "Dropbox-Keys", ofType: "plist")
+        let url = URL(fileURLWithPath: path!)
+        let keys = try! NSDictionary(contentsOf: url, error: ())
+
+        DropboxClientsManager.setupWithAppKey(keys["DROPBOX_APP_KEY"] as! String)
+    }
+
+    public func unlink() {
+        DropboxClientsManager.unlinkClients()
+    }
+
+    public func isAuthorized() -> Bool {
+        DropboxClientsManager.authorizedClient != nil
+    }
+
+    public func authorize(presenting controller: UIViewController) -> AnyPublisher<Result<Bool, Error>, Never> {
+        let scopes = ["files.metadata.read", "files.content.read", "files.content.write"]
+
+        return didAuthorizeSubject.handleEvents(receiveSubscription: { _ in
+            let scopeRequest = ScopeRequest(scopeType: .user, scopes: scopes, includeGrantedScopes: false)
+
+            DropboxClientsManager.authorizeFromControllerV2(
+                UIApplication.shared,
+                controller: controller,
+                loadingStatusDelegate: nil,
+                openURL: { (url: URL) -> Void in UIApplication.shared.open(url, options: [:], completionHandler: nil) },
+                scopeRequest: scopeRequest
+            )
+        }).first().eraseToAnyPublisher()
+    }
+
+    public func handleOpenUrl(_ url: URL) -> Bool {
+        DropboxClientsManager.handleRedirectURL(url) {
+            switch $0 {
+            case .none:
+                didAuthorizeSubject.send(.success(false))
+            case .error(let oAuthError, _):
+                didAuthorizeSubject.send(.failure(oAuthError))
+            case .success:
+                didAuthorizeSubject.send(.success(true))
+            case .cancel:
+                didAuthorizeSubject.send(.success(false))
+            }
+        }
+    }
+
+    public func downloadBackup(_ path: String, _ completion: @escaping (Result<Data, Error>) -> Void) {
+        Task {
+            do {
+                guard try await folderExists() else { fatalError() }
+
+                let data = try await fetchBackup()
+                completion(.success(data))
+            } catch {
+                completion(.failure(error))
+            }
+        }
+    }
+
+    public func uploadBackup(_ url: URL, _ completion: @escaping (Result<DropboxMetadata, Error>) -> Void) {
+        Task {
+            do {
+                if try await !folderExists() {
+                    try await createFolder()
+                }
+
+                let data = try Data(contentsOf: url)
+                let metadata = try await upload(data: data)
+                completion(.success(metadata))
+            } catch {
+                completion(.failure(error))
+            }
+        }
+    }
+
+    public func downloadMetadata(_ completion: @escaping (Result<DropboxMetadata?, Error>) -> Void) {
+        Task {
+            do {
+                guard try await folderExists() else {
+                    completion(.success(nil))
+                    return
+                }
+
+                let metadata = try await fetchMetadata()
+                completion(.success(metadata))
+            } catch {
+                completion(.failure(error))
+            }
+        }
+    }
+}
+
+extension DropboxService {
+    private func folderExists() async throws -> Bool {
+        guard let client = DropboxClientsManager.authorizedClient else { fatalError() }
+
+        return try await withCheckedThrowingContinuation { continuation in
+            client.files.listFolder(path: "/backup")
+                .response { result, error in
+                if let error = error {
+                    if case .routeError(_, _, _, _) = error as CallError {
+                        continuation.resume(returning: false)
+                        return
+                    }
+
+                    let err = NSError(domain: error.description, code: 0)
+                    continuation.resume(throwing: err)
+                    return
+                }
+
+                continuation.resume(returning: result != nil)
+            }
+        }
+    }
+
+    private func createFolder() async throws {
+        guard let client = DropboxClientsManager.authorizedClient else { fatalError() }
+
+        return try await withCheckedThrowingContinuation { continuation in
+            client.files.createFolderV2(path: "/backup")
+                .response { _, error in
+                if let error = error {
+                    let err = NSError(domain: error.description, code: 0)
+                    continuation.resume(throwing: err)
+                    return
+                }
+
+                continuation.resume(returning: ())
+            }
+        }
+    }
+
+    private func fetchMetadata() async throws -> DropboxMetadata? {
+        guard let client = DropboxClientsManager.authorizedClient else { fatalError() }
+
+        return try await withCheckedThrowingContinuation { continuation in
+            client.files.getMetadata(path: "/backup/backup.xxm")
+                .response { response, error in
+                if let error = error {
+                    let err = NSError(domain: error.description, code: 0)
+                    continuation.resume(throwing: err)
+                    return
+                }
+
+                if let result = response as? Files.FileMetadata {
+                    let size = Float(result.size)
+                    let modifiedDate = result.serverModified
+                    continuation.resume(returning: .init(
+                        size: size,
+                        path: "/backup/backup.xxm",
+                        modifiedDate: modifiedDate
+                    ))
+                } else {
+                    continuation.resume(returning: nil)
+                }
+            }
+        }
+    }
+
+    private func fetchBackup() async throws -> Data {
+        guard let client = DropboxClientsManager.authorizedClient else { fatalError() }
+
+        return try await withCheckedThrowingContinuation { continuation in
+            client.files.download(path: "/backup/backup.xxm")
+                .response(completionHandler: { response, error in
+                if let error = error {
+                    let err = NSError(domain: error.description, code: 0)
+                    continuation.resume(throwing: err)
+                    return
+                }
+
+                if let response = response {
+                    continuation.resume(returning: response.1)
+                }
+            })
+        }
+    }
+
+    private func upload(data: Data) async throws -> DropboxMetadata {
+        guard let client = DropboxClientsManager.authorizedClient else { fatalError() }
+
+        return try await withCheckedThrowingContinuation { continuation in
+            client.files.upload(path: "/backup/backup.xxm", mode: .overwrite, input: data)
+                .response { response, error in
+                    if let error = error {
+                        let err = NSError(domain: error.description, code: 0)
+                        continuation.resume(throwing: err)
+                        return
+                    }
+
+                    if let response = response {
+                        continuation.resume(returning: .init(
+                            size: Float(response.size),
+                            path: response.pathLower!,
+                            modifiedDate: response.serverModified
+                        ))
+                    }
+                }
+        }
+    }
+}
diff --git a/Sources/DropboxFeature/DropboxServiceMock.swift b/Sources/DropboxFeature/DropboxServiceMock.swift
new file mode 100644
index 0000000000000000000000000000000000000000..ba9654b69f870fb28b49cd8414676375fcb866ba
--- /dev/null
+++ b/Sources/DropboxFeature/DropboxServiceMock.swift
@@ -0,0 +1,22 @@
+import UIKit
+import Combine
+
+public struct DropboxServiceMock: DropboxInterface {
+    public init() {}
+
+    public func unlink() {}
+
+    public func isAuthorized() -> Bool { true }
+
+    public func handleOpenUrl(_ url: URL) -> Bool { true }
+
+    public func didFinishAuthFlow(withError: String?) {}
+
+    public func downloadBackup(_: String, _: @escaping (Result<Data, Error>) -> Void) {}
+
+    public func uploadBackup(_: URL, _: @escaping (Result<DropboxMetadata, Error>) -> Void) {}
+
+    public func downloadMetadata(_: @escaping (Result<DropboxMetadata?, Error>) -> Void) {}
+
+    public func authorize(presenting: UIViewController) -> AnyPublisher<Result<Bool, Error>, Never> { fatalError() }
+}
diff --git a/Sources/DropboxFeature/Resources/Dropbox-Keys.plist b/Sources/DropboxFeature/Resources/Dropbox-Keys.plist
new file mode 100644
index 0000000000000000000000000000000000000000..ecf15d0188386cac204a2e243e59320620a6c9c5
--- /dev/null
+++ b/Sources/DropboxFeature/Resources/Dropbox-Keys.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>DROPBOX_APP_KEY</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Sources/GoogleDriveFeature/GoogleDriveInterface.swift b/Sources/GoogleDriveFeature/GoogleDriveInterface.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c6710b8610fc9081e211fe0363f10f40584ea0e6
--- /dev/null
+++ b/Sources/GoogleDriveFeature/GoogleDriveInterface.swift
@@ -0,0 +1,13 @@
+import UIKit
+
+public protocol GoogleDriveInterface {
+    func isAuthorized(_: @escaping (Bool) -> Void)
+
+    func downloadMetadata(_: @escaping (Result<GoogleDriveMetadata?, Error>) -> Void)
+
+    func uploadBackup(_: URL, _: @escaping (Result<GoogleDriveMetadata, Error>) -> Void)
+
+    func authorize(presenting: UIViewController, _: @escaping (Result<Void, Error>) -> Void)
+
+    func downloadBackup(_: String, progressCallback: @escaping (Float) -> Void, _: @escaping (Result<Data, Error>) -> Void)
+}
diff --git a/Sources/GoogleDriveFeature/GoogleDriveMetadata.swift b/Sources/GoogleDriveFeature/GoogleDriveMetadata.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f4179db86b943af99a0eaacdcae32ea987bdd73f
--- /dev/null
+++ b/Sources/GoogleDriveFeature/GoogleDriveMetadata.swift
@@ -0,0 +1,27 @@
+import GoogleAPIClientForREST_Drive
+
+public struct GoogleDriveMetadata: Equatable {
+    public var size: Float
+    public var identifier: String
+    public var modifiedDate: Date
+
+    public init(
+        size: Float,
+        identifier: String,
+        modifiedDate: Date
+    ) {
+        self.size = size
+        self.identifier = identifier
+        self.modifiedDate = modifiedDate
+    }
+}
+
+extension GoogleDriveMetadata {
+    init?(withDriveFile file: GTLRDrive_File) {
+        guard let size = file.size?.floatValue,
+              let identifier = file.identifier,
+              let modifiedDate = file.modifiedTime?.date else { return nil }
+
+        self.init(size: size, identifier: identifier, modifiedDate: modifiedDate)
+    }
+}
diff --git a/Sources/GoogleDriveFeature/GoogleDriveService.swift b/Sources/GoogleDriveFeature/GoogleDriveService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..4ae4ac7457058d11ab3777cd71a4e8843f33458c
--- /dev/null
+++ b/Sources/GoogleDriveFeature/GoogleDriveService.swift
@@ -0,0 +1,335 @@
+import UIKit
+import GoogleSignIn
+import GTMSessionFetcherFull
+import GTMSessionFetcherCore
+import GoogleAPIClientForREST_Drive
+
+public final class GoogleDriveService: GoogleDriveInterface {
+    private static let scopeFile = "https://www.googleapis.com/auth/drive.file"
+    private static let scopeAppData = "https://www.googleapis.com/auth/drive.appdata"
+
+    var user: GIDGoogleUser?
+
+    let service: GTLRDriveService = {
+        let service = GTLRDriveService()
+
+        let path = Bundle.module.path(forResource: "GoogleDrive-Keys", ofType: "plist")
+        let url = URL(fileURLWithPath: path!)
+        let keys = try! NSDictionary(contentsOf: url, error: ())
+
+        service.apiKey = keys["DRIVE_API_KEY"] as? String
+        return service
+    }()
+
+    public init() {}
+
+    public func isAuthorized(_ completion: @escaping (Bool) -> Void) {
+        guard GIDSignIn.sharedInstance.hasPreviousSignIn() else {
+            return completion(false)
+        }
+
+        GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
+            guard let user = user, let scopes = user.grantedScopes, error == nil else {
+                return completion(false)
+            }
+
+            self.user = user
+            self.service.authorizer = user.authentication.fetcherAuthorizer()
+            completion(scopes.contains(GoogleDriveService.scopeFile) && scopes.contains(GoogleDriveService.scopeAppData))
+        }
+    }
+
+    public func authorize(
+        presenting controller: UIViewController,
+        _ completion: @escaping (Result<Void, Error>) -> Void
+    ) {
+        GIDSignIn.sharedInstance.restorePreviousSignIn { [weak self] user, error in
+            guard let self = self else { return }
+
+            guard error == nil else {
+                self.signIn(presenting: controller) {
+                    switch $0 {
+                    case .success:
+                        self.authorizeDrive(controller: controller, completion: completion)
+                    case .failure(let error):
+                        completion(.failure(error))
+                    }
+                }
+
+                return
+            }
+
+            guard let user = user else { fatalError() }
+
+            self.user = user
+            self.service.authorizer = user.authentication.fetcherAuthorizer()
+            self.authorizeDrive(controller: controller, completion: completion)
+        }
+    }
+
+    public func downloadMetadata(_ completion: @escaping (Result<GoogleDriveMetadata?, Error>) -> Void) {
+        Task {
+            do {
+                guard let folder = try await fetchFolder() else {
+                    completion(.success(nil))
+                    return
+                }
+
+                _ = try await listFiles(on: folder)
+
+                let backup = try await fetchBackup(at: folder)
+                completion(.success(backup))
+            } catch {
+                completion(.failure(error))
+            }
+        }
+    }
+
+    public func downloadBackup(
+        _ backup: String,
+        progressCallback: @escaping (Float) -> Void,
+        _ completion: @escaping (Result<Data, Error>) -> Void
+    ) {
+        let query = GTLRDriveQuery_FilesGet.queryForMedia(withFileId: backup)
+        service.executeQuery(query) { _, file, error in
+            guard error == nil else {
+                print("Error on line #\(#line): \(error!.localizedDescription)")
+                return completion(.failure(error!))
+            }
+
+            guard let data = (file as? GTLRDataObject)?.data else {
+                print("Error on line #\(#line)")
+                return completion(.failure(NSError()))
+            }
+
+            completion(.success(data))
+        }
+    }
+
+    public func uploadBackup(
+        _ file: URL,
+        _ completion: @escaping (Result<GoogleDriveMetadata, Error>) -> Void
+    ) {
+        Task {
+            do {
+                var folder = try await fetchFolder()
+                if folder == nil { folder = try await createFolder() }
+                let metadata = try await uploadFile(file, to: folder!)
+                let listMetadata = try await listFiles(on: folder!)
+                try await cleanup(listMetadata)
+                completion(.success(metadata))
+            } catch {
+                print("Error on line #\(#line): \(error.localizedDescription)")
+                completion(.failure(error))
+            }
+        }
+    }
+}
+
+extension GoogleDriveService {
+    private func authorizeDrive(
+        controller: UIViewController,
+        completion: @escaping (Result<Void, Error>) -> Void
+    ) {
+        if let user = user,
+           let scopes = user.grantedScopes,
+           scopes.contains(GoogleDriveService.scopeFile),
+           scopes.contains(GoogleDriveService.scopeAppData) {
+            return completion(.success(()))
+        }
+
+        GIDSignIn.sharedInstance.addScopes(
+            [GoogleDriveService.scopeFile, GoogleDriveService.scopeAppData],
+            presenting: controller, callback: { user, error in
+                guard error == nil else {
+                    print("Error on line #\(#line): \(error!.localizedDescription)")
+                    return completion(.failure(error!))
+                }
+
+                guard let user = user else { fatalError() }
+                self.user = user
+                completion(.success(()))
+            }
+        )
+    }
+
+    private func signIn(
+        presenting controller: UIViewController,
+        completion: @escaping (Result<Void, Error>) -> Void
+    ) {
+        GIDSignIn.sharedInstance.signIn(
+            with: GIDConfiguration(clientID: "662236151640-30i07ubg6ukodg15u0bnpk322p030u3j.apps.googleusercontent.com"),
+            presenting: controller,
+            callback: { user, error in
+                guard error == nil else {
+                    print("Error on line #\(#line): \(error!.localizedDescription)")
+                    return completion(.failure(error!))
+                }
+
+                guard let user = user else { fatalError() }
+
+                self.user = user
+                self.service.authorizer = user.authentication.fetcherAuthorizer()
+                completion(.success(()))
+            }
+        )
+    }
+
+    private func fetchFolder() async throws -> String? {
+        let query = GTLRDriveQuery_FilesList.query()
+        query.q = "mimeType = 'application/vnd.google-apps.folder' and name = 'backup'"
+        query.spaces = "appDataFolder"
+        query.fields = "nextPageToken, files(id, name)"
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, result, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                let item = (result as? GTLRDrive_FileList)?.files?.first
+                continuation.resume(returning: item?.identifier)
+            }
+        }
+    }
+
+    private func fetchBackup(at folder: String) async throws -> GoogleDriveMetadata? {
+        let query = GTLRDriveQuery_FilesList.query()
+        query.q = "'\(folder)' in parents and name = 'backup.xxm'"
+        query.spaces = "appDataFolder"
+        query.fields = "nextPageToken, files(id, size, name, modifiedTime)"
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, result, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                var metadata: GoogleDriveMetadata? = nil
+
+                if let file = (result as? GTLRDrive_FileList)?.files?.first,
+                   let size = file.size,
+                   let id = file.identifier,
+                   let date = file.modifiedTime?.date {
+                    metadata = GoogleDriveMetadata(size: size.floatValue, identifier: id, modifiedDate: date)
+                }
+
+                continuation.resume(returning: metadata)
+            }
+        }
+    }
+
+    private func createFolder() async throws -> String {
+        let file = GTLRDrive_File()
+        file.name = "backup"
+        file.parents = ["appDataFolder"]
+        file.mimeType = "application/vnd.google-apps.folder"
+
+        let query = GTLRDriveQuery_FilesCreate.query(withObject: file, uploadParameters: nil)
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, result, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                guard let identifier = (result as? GTLRDrive_File)?.identifier else {
+                    let errorTitle = "Couldn't create backup folder but no error was passed (?)"
+                    let error = NSError(domain: errorTitle, code: 0, userInfo: [NSLocalizedDescriptionKey: errorTitle])
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                continuation.resume(returning: identifier)
+            }
+        }
+    }
+
+    private func uploadFile(
+        _ fileURL: URL,
+        to folder: String
+    ) async throws -> GoogleDriveMetadata {
+
+        let file = GTLRDrive_File()
+        file.name = "backup.xxm"
+        file.parents = [folder]
+        file.mimeType = "application/octet-stream"
+
+        let params = GTLRUploadParameters(fileURL: fileURL, mimeType: file.mimeType!)
+        let query = GTLRDriveQuery_FilesCreate.query(withObject: file, uploadParameters: params)
+        query.fields = "id, size, modifiedTime"
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, result, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                guard let driveFile = (result as? GTLRDrive_File),
+                      let size = driveFile.size,
+                      let id = driveFile.identifier,
+                      let date = driveFile.modifiedTime?.date else {
+                    let errorTitle = "Couldn't upload file but no error was passed (?)"
+                    let error = NSError(domain: errorTitle, code: 0, userInfo: [NSLocalizedDescriptionKey: errorTitle])
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                continuation.resume(returning: .init(size: size.floatValue, identifier: id, modifiedDate: date))
+            }
+        }
+    }
+
+    private func listFiles(on folder: String) async throws -> [GoogleDriveMetadata] {
+        let query = GTLRDriveQuery_FilesList.query()
+        query.q = "'\(folder)' in parents"
+        query.spaces = "appDataFolder"
+        query.fields = "nextPageToken, files(id, modifiedTime, size, name)"
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, result, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                guard let files = (result as? GTLRDrive_FileList)?.files else {
+                    continuation.resume(returning: [])
+                    return
+                }
+
+                let metadataList = files.compactMap(GoogleDriveMetadata.init(withDriveFile:))
+                continuation.resume(returning: metadataList)
+            }
+        }
+    }
+
+    private func cleanup(_ files: [GoogleDriveMetadata]) async throws {
+        let latestBackup = files.max { $0.modifiedDate < $1.modifiedDate }
+        let identifiers = files.filter { $0 != latestBackup }.map(\.identifier)
+        let query = GTLRBatchQuery(queries: identifiers.map(GTLRDriveQuery_FilesDelete.query(withFileId:)))
+
+        return try await withCheckedThrowingContinuation { continuation in
+            service.executeQuery(query) { _, _, error in
+                if let error = error {
+                    print("Error on line #\(#line): \(error.localizedDescription)")
+                    continuation.resume(throwing: error)
+                    return
+                }
+
+                continuation.resume(returning: ())
+            }
+        }
+    }
+}
diff --git a/Sources/GoogleDriveFeature/GoogleDriveServiceMock.swift b/Sources/GoogleDriveFeature/GoogleDriveServiceMock.swift
new file mode 100644
index 0000000000000000000000000000000000000000..cf7625355cbc778fca29eb245586079b1601db6f
--- /dev/null
+++ b/Sources/GoogleDriveFeature/GoogleDriveServiceMock.swift
@@ -0,0 +1,40 @@
+import UIKit
+
+public final class GoogleDriveServiceMock: GoogleDriveInterface {
+    public init() {}
+
+    public func isAuthorized(_ completion: @escaping (Bool) -> Void) {
+        completion(true)
+    }
+
+    public func uploadBackup(_: URL, _ completion: @escaping (Result<GoogleDriveMetadata, Error>) -> Void) {
+        completion(.success(.init(size: 23.toBytes(), identifier: "", modifiedDate: Date())))
+    }
+
+    public func downloadMetadata(_ completion: @escaping (Result<GoogleDriveMetadata?, Error>) -> Void) {
+        completion(.success(.init(size: 23.toBytes(), identifier: "", modifiedDate: Date())))
+    }
+
+    public func authorize(presenting: UIViewController, _ completion: @escaping (Result<Void, Error>) -> Void) {
+        completion(.success(()))
+    }
+
+    public func downloadBackup(
+        _: String,
+        progressCallback: @escaping (Float) -> Void,
+        _ completion: @escaping (Result<Data, Error>) -> Void
+    ) {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { progressCallback(3.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { progressCallback(7.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) { progressCallback(12.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { progressCallback(15.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { progressCallback(16.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) { progressCallback(19.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 2.1) { progressCallback(22.toBytes()) }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 2.4) { completion(.success(Data())) }
+    }
+}
+
+private extension Int {
+    func toBytes() -> Float { Float(self) * 1000000.0 }
+}
diff --git a/Sources/GoogleDriveFeature/Resources/GoogleDrive-Keys.plist b/Sources/GoogleDriveFeature/Resources/GoogleDrive-Keys.plist
new file mode 100644
index 0000000000000000000000000000000000000000..614bac60d3fd5014a33613ee3e83c72656d0854d
--- /dev/null
+++ b/Sources/GoogleDriveFeature/Resources/GoogleDrive-Keys.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>DRIVE_API_KEY</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Sources/Integration/Callbacks.swift b/Sources/Integration/Callbacks.swift
index 5f658bc3ce6b2bbdb427153f8f45a7554a0e2429..ed9d917cf8ff0469015a0dfa4db869a450666bfe 100644
--- a/Sources/Integration/Callbacks.swift
+++ b/Sources/Integration/Callbacks.swift
@@ -102,7 +102,7 @@ final class RoundCallback: NSObject, BindingsRoundCompletionCallbackProtocol {
     }
 
     func eventCallback(_ rid: Int, success: Bool, timedOut: Bool) {
-        log(string: "Add/Confirm RoundCallback:\nid: \(rid)\nSuccessfull: \(success)\nTimed out: \(timedOut)", type: .info)
+        log(string: ">>> Add/Confirm RoundCallback:\nid: \(rid)\nSuccessfull: \(success)\nTimed out: \(timedOut)", type: .info)
         callback(success && !timedOut)
     }
 }
@@ -203,17 +203,14 @@ final class LookupCallback: NSObject, BindingsLookupCallbackProtocol {
     }
 
     func callback(_ contact: BindingsContact?, error: String?) {
-        guard let contact = contact else {
-            if let error = error {
-                if !error.isEmpty {
-                    callback(.failure(NSError.create(error).friendly()))
-                }
-            }
-
+        if let error = error, !error.isEmpty {
+            callback(.failure(NSError.create(error).friendly()))
             return
         }
 
-        callback(.success(contact))
+        if let contact = contact {
+            callback(.success(contact))
+        }
     }
 }
 
@@ -255,3 +252,44 @@ final class OutgoingTransferProgressCallback: NSObject, BindingsFileTransferSent
         callback(completed, sent, arrived, total, err)
     }
 }
+
+final class UpdateBackupCallback: NSObject, BindingsUpdateBackupFuncProtocol {
+    let callback: (Data) -> Void
+
+    init(_ callback: @escaping (Data) -> Void) {
+        self.callback = callback
+        super.init()
+    }
+
+    func updateBackup(_ encryptedBackup: Data?) {
+        guard let data = encryptedBackup else { return }
+        callback(data)
+    }
+}
+
+final class ResetCallback: NSObject, BindingsAuthResetNotificationCallbackProtocol {
+    let callback: (BindingsContact) -> Void
+
+    init(_ callback: @escaping (BindingsContact) -> Void) {
+        self.callback = callback
+        super.init()
+    }
+
+    func callback(_ requestor: BindingsContact?) {
+        guard let requestor = requestor else { return }
+        callback(requestor)
+    }
+}
+
+final class RestoreContactsCallback: NSObject, BindingsRestoreContactsUpdaterProtocol {
+    let callback: (Int, Int, Int, String?) -> Void
+
+    init(_ callback: @escaping (Int, Int, Int, String?) -> Void) {
+        self.callback = callback
+        super.init()
+    }
+
+    func restoreContactsCallback(_ numFound: Int, numRestored: Int, total: Int, err: String?) {
+        callback(numFound, numRestored, total, err)
+    }
+}
diff --git a/Sources/Integration/Client.swift b/Sources/Integration/Client.swift
index 22d4a68f2167c6d5a9e09919bae19b076c7621fd..690f6132f7ed344d74dcde0014778fc464f71dce 100644
--- a/Sources/Integration/Client.swift
+++ b/Sources/Integration/Client.swift
@@ -3,42 +3,65 @@ import Models
 import Combine
 import Defaults
 import Foundation
+import Bindings
 
 public class Client {
     @KeyObject(.inappnotifications, defaultValue: true) var inappnotifications: Bool
 
     let bindings: BindingsInterface
+    var backupManager: BackupInterface?
     var dummyManager: DummyTrafficManaging?
     var groupManager: GroupManagerInterface?
     var userDiscovery: UserDiscoveryInterface?
     var transferManager: TransferManagerInterface?
 
+    var backup: AnyPublisher<Data, Never> { backupSubject.eraseToAnyPublisher() }
     var network: AnyPublisher<Bool, Never> { networkSubject.eraseToAnyPublisher() }
+    var resets: AnyPublisher<Contact, Never> { resetsSubject.eraseToAnyPublisher() }
     var messages: AnyPublisher<Message, Never> { messagesSubject.eraseToAnyPublisher() }
     var requests: AnyPublisher<Contact, Never> { requestsSubject.eraseToAnyPublisher() }
     var events: AnyPublisher<BackendEvent, Never> { eventsSubject.eraseToAnyPublisher() }
+    var requestsSent: AnyPublisher<Contact, Never> { requestsSentSubject.eraseToAnyPublisher() }
     var confirmations: AnyPublisher<Contact, Never> { confirmationsSubject.eraseToAnyPublisher() }
     var groupMessages: AnyPublisher<GroupMessage, Never> { groupMessagesSubject.eraseToAnyPublisher() }
     var incomingTransfers: AnyPublisher<FileTransfer, Never> { transfersSubject.eraseToAnyPublisher() }
     var groupRequests: AnyPublisher<(Group, [Data], String?), Never> { groupRequestsSubject.eraseToAnyPublisher() }
 
+    private let backupSubject = PassthroughSubject<Data, Never>()
     private let networkSubject = PassthroughSubject<Bool, Never>()
+    private let resetsSubject = PassthroughSubject<Contact, Never>()
     private let requestsSubject = PassthroughSubject<Contact, Never>()
     private let messagesSubject = PassthroughSubject<Message, Never>()
     private let eventsSubject = PassthroughSubject<BackendEvent, Never>()
+    private let requestsSentSubject = PassthroughSubject<Contact, Never>()
     private let confirmationsSubject = PassthroughSubject<Contact, Never>()
     private let transfersSubject = PassthroughSubject<FileTransfer, Never>()
     private let groupMessagesSubject = PassthroughSubject<GroupMessage, Never>()
     private let groupRequestsSubject = PassthroughSubject<(Group, [Data], String?), Never>()
 
+    private var isBackupInitialization = false
+    private var isBackupInitializationCompleted = false
+
     // MARK: Lifecycle
 
-    init(_ bindings: BindingsInterface) {
+    init(
+        _ bindings: BindingsInterface,
+        fromBackup: Bool,
+        email: String?,
+        phone: String?
+    ) {
         self.bindings = bindings
+        self.isBackupInitialization = fromBackup
 
         do {
             try registerListenersAndStart()
-            try instantiateUserDiscovery()
+
+            if fromBackup {
+                try instantiateUserDiscoveryFromBackup(email: email, phone: phone)
+            } else {
+                try instantiateUserDiscovery()
+            }
+
             try instantiateTransferManager()
             try instantiateDummyTrafficManager()
             updatePreImage()
@@ -47,18 +70,81 @@ public class Client {
         }
     }
 
-    // MARK: Public
+    public func listenBackup() {
+        backupManager = nil
+        backupManager = bindings.listenBackups { [weak backupSubject] in
+            backupSubject?.send($0)
+        }
+    }
+
+    public func addJson(_ string: String) {
+        guard let backupManager = backupManager else { return }
+        backupManager.addJson(string)
+    }
 
-    private func registerListenersAndStart() throws {
-        bindings.listenNetworkUpdates { [weak networkSubject] in
-            networkSubject?.send($0)
+    public func stopListeningBackup() {
+        guard let backupManager = backupManager else { return }
+        try? backupManager.stop()
+        self.backupManager = nil
+    }
+
+    public func restoreContacts(fromBackup backup: Data) {
+        var totalPendingRestoration: Int = 0
+
+        let report = bindings.restore(
+            ids: backup,
+            using: userDiscovery!) { [weak self] in
+                guard let self = self else { return }
+
+                switch $0 {
+                case .success(var contact):
+                    contact.status = .requested
+                    self.requestsSentSubject.send(contact)
+                    print(">>> Restored \(contact.username). Setting status as requested")
+                case .failure(let error):
+                    print(">>> \(error.localizedDescription)")
+                }
+            } restoreCallback: { numFound, numRestored, total, errorString in
+                totalPendingRestoration = total
+                let results =
+            """
+            >>> Results from within closure of RestoreContacts:
+            - numFound: \(numFound)
+            - numRestored: \(numRestored)
+            - total: \(total)
+            - errorString: \(errorString)
+            """
+                print(results)
+            }
+
+        guard totalPendingRestoration > 0 else { fatalError("Total is zero, why called restore contacts?") }
+
+        guard report.lenRestored() == totalPendingRestoration else {
+            print(">>> numRestored \(report.lenRestored()) is != than the total (\(totalPendingRestoration)). Going on recursion...\nnumFailed: \(report.lenFailed())\n\(report.getRestoreContactsError())")
+            restoreContacts(fromBackup: backup)
+            return
         }
 
+        isBackupInitializationCompleted = true
+    }
+
+    private func registerListenersAndStart() throws {
+        bindings.listenNetworkUpdates { [weak networkSubject] in networkSubject?.send($0) }
+
         bindings.listenRequests { [weak self] in
             guard let self = self else { return }
-            self.requestsSubject.send($0)
-        } confirmations: { [weak confirmationsSubject] in
+
+            if self.isBackupInitialization {
+                if self.isBackupInitializationCompleted {
+                    self.requestsSubject.send($0)
+                }
+            } else {
+                self.requestsSubject.send($0)
+            }
+        } _: { [weak confirmationsSubject] in
             confirmationsSubject?.send($0)
+        } _: { [weak resetsSubject] in
+            resetsSubject?.send($0)
         }
 
         bindings.listenEvents { [weak eventsSubject] in
@@ -115,6 +201,13 @@ public class Client {
         }
     }
 
+    private func instantiateUserDiscoveryFromBackup(email: String?, phone: String?) throws {
+        retry(max: 4, retryStrategy: .delay(seconds: 1)) { [weak self] in
+            guard let self = self else { return }
+            self.userDiscovery = try self.bindings.generateUDFromBackup(email: email, phone: phone)
+        }
+    }
+
     private func instantiateDummyTrafficManager() throws {
         dummyManager = try bindings.generateDummyTraficManager()
     }
diff --git a/Sources/Integration/Implementations/Bindings.swift b/Sources/Integration/Implementations/Bindings.swift
index f6a5fc67754e8c60211cc3edd5beeeb361763f1e..e9766771e889f1df7725c7b55295a9ac9a113249 100644
--- a/Sources/Integration/Implementations/Bindings.swift
+++ b/Sources/Integration/Implementations/Bindings.swift
@@ -49,6 +49,16 @@ extension BindingsClient: BindingsInterface {
         log(string: string, type: .bindings)
     }
 
+    public func resetSessionWith(_ recipient: Data) {
+        var int: Int = 0
+
+        do {
+            try resetSession(recipient, meMarshaled: meMarshalled, message: "", ret0_: &int)
+        } catch {
+            print(">>> \(error.localizedDescription)")
+        }
+    }
+
     public func verify(marshaled: Data, verifiedMarshaled: Data) throws -> Bool {
         var bool: ObjCBool = false
         try verifyOwnership(marshaled, verifiedMarshaled: verifiedMarshaled, ret0_: &bool)
@@ -181,12 +191,14 @@ extension BindingsClient: BindingsInterface {
         return BindingsGetVersion()
     }()
 
+    public static let new: ClientNew = BindingsNewClient
+
+    public static let fromBackup: ClientFromBackup = BindingsNewClientFromBackup
+
     public static let secret: (Int) -> Data? = BindingsGenerateSecret
 
     public static let login: (String?, Data?, String?, NSErrorPointer) -> BindingsInterface? = BindingsLogin
 
-    public static let newClient: (String?, String?, Data?, String?, NSErrorPointer) -> Bool = BindingsNewClient
-
     public static func updateNDF(
         for env: NetworkEnvironment,
         _ completion: @escaping (Result<Data?, Error>) -> Void
@@ -246,14 +258,14 @@ extension BindingsClient: BindingsInterface {
         registerErrorCallback(BindingsError())
 
         guard status == 0 else {
-            log(string: "Network is not ready yet. Let's give it a second...", type: .error)
+            log(string: ">>> Network is not ready yet. Let's give it a second...", type: .error)
             sleep(1)
             startNetwork()
             return
         }
 
         try! startNetworkFollower(10000)
-        log(string: "Starting the network...", type: .info)
+        log(string: ">>> Starting the network...", type: .info)
     }
 
     /// (Tries) to stop the network
@@ -528,22 +540,79 @@ extension BindingsClient: BindingsInterface {
         throw error.friendly()
     }
 
-    /// Instantiates user discovery
-    ///
-    /// - Returns: An instance of *UD (User discovery)*
-    ///
-    /// - Throws: `UDError.noInstance` if no error was thrown
-    ///            but also no instance was created
-    ///
+    public func generateUDFromBackup(email: String?, phone: String?) throws -> UserDiscoveryInterface {
+        var error: NSError?
+
+        let paramEmail = email != nil ? "E\(email!)" : nil
+        let paramPhone = phone != nil ? "P\(phone!)" : nil
+
+        let udb = BindingsNewUserDiscoveryFromBackup(self, paramEmail, paramPhone, &error)
+
+        /// Alternate udb
+
+//        guard let certPath = Bundle.module.path(forResource: "ud.elixxir.io", ofType: "crt") else {
+//            fatalError("Couldn't retrieve cert.")
+//        }
+//
+//        guard let contactFilePath = Bundle.module.path(forResource: "udContact-test", ofType: "bin") else {
+//            fatalError("Couldn't retrieve cert.")
+//        }
+//
+//        try! udb!.setAlternative(
+//            "18.198.117.203:11420".data(using: .utf8),
+//            cert: try! Data(contentsOf: URL(fileURLWithPath: certPath)),
+//            contactFile: try! Data(contentsOf: URL(fileURLWithPath: contactFilePath))
+//        )
+
+        guard let error = error else { return udb! }
+        throw error.friendly()
+    }
+
     public func generateUD() throws -> UserDiscoveryInterface {
         log(type: .crumbs)
 
         var error: NSError?
         let udb = BindingsNewUserDiscovery(self, &error)
 
+        /// Alternate udb
+
+//        guard let certPath = Bundle.module.path(forResource: "ud.elixxir.io", ofType: "crt") else {
+//            fatalError("Couldn't retrieve cert.")
+//        }
+//
+//        guard let contactFilePath = Bundle.module.path(forResource: "udContact-test", ofType: "bin") else {
+//            fatalError("Couldn't retrieve cert.")
+//        }
+//
+//        try! udb!.setAlternative(
+//            "18.198.117.203:11420".data(using: .utf8),
+//            cert: try! Data(contentsOf: URL(fileURLWithPath: certPath)),
+//            contactFile: try! Data(contentsOf: URL(fileURLWithPath: contactFilePath))
+//        )
+
         guard let error = error else { return udb! }
         throw error.friendly()
     }
+
+    public func restore(
+        ids: Data,
+        using ud: UserDiscoveryInterface,
+        lookupCallback: @escaping (Result<Contact, Error>) -> Void,
+        restoreCallback: @escaping (Int, Int, Int, String?) -> Void
+    ) -> RestoreReportType {
+        let restoreCb = RestoreContactsCallback(restoreCallback)
+
+        let lookupCb = LookupCallback {
+            switch $0 {
+            case .success(let contact):
+                lookupCallback(.success(.init(with: contact, status: .stranger)))
+            case .failure(let error):
+                lookupCallback(.failure(error))
+            }
+        }
+
+        return BindingsRestoreContactsFromBackup(ids, self, ud as? BindingsUserDiscovery, lookupCb, restoreCb)!
+    }
 }
 
 extension BindingsContact {
@@ -585,7 +654,6 @@ extension BindingsSendReport: E2ESendReportType {
     public var roundURL: String { getRoundURL() }
 }
 
-
 public protocol DummyTrafficManaging {
     var status: Bool { get }
     func setStatus(status: Bool)
@@ -600,3 +668,7 @@ extension BindingsDummyTraffic: DummyTrafficManaging {
         try? setStatus(status)
     }
 }
+
+extension BindingsBackup: BackupInterface {}
+
+extension BindingsRestoreContactsReport: RestoreReportType {}
diff --git a/Sources/Integration/Implementations/UserDiscovery.swift b/Sources/Integration/Implementations/UserDiscovery.swift
index 98e584e7d8096df3aca0a053b82ac1b2c833944e..b88afa0d8585ef62648eb0af7fe86160fff18b8c 100644
--- a/Sources/Integration/Implementations/UserDiscovery.swift
+++ b/Sources/Integration/Implementations/UserDiscovery.swift
@@ -1,3 +1,4 @@
+import Retry
 import Models
 import Bindings
 import Foundation
@@ -9,23 +10,20 @@ extension BindingsUserDiscovery: UserDiscoveryInterface {
             case .success(let contact):
                 completion(.success(.init(with: contact, status: .stranger)))
             case .failure(let error):
-                log(string: "UD.lookup 4E2E callback failed:\n\(error.localizedDescription)", type: .error)
                 completion(.failure(error))
             }
         }
 
-        do {
-            try lookup(forUserId, callback: callback, timeoutMS: 20000)
-        } catch {
+        retry(max: 10, retryStrategy: .delay(seconds: 1)) { [weak self] in
+            guard let self = self else { return }
+            try self.lookup(forUserId, callback: callback, timeoutMS: 20000)
+        }.finalCatch { error in
             log(string: "UD.lookup 4E2E failed:\n\(error.localizedDescription)", type: .error)
             completion(.failure(error.friendly()))
         }
     }
 
-    public func lookup(
-        idList: [Data],
-        _ completion: @escaping (Result<[LookupResult], Error>) -> Void
-    ) {
+    public func lookup(idList: [Data], _ completion: @escaping (Result<[Contact], Error>) -> Void) {
         let list = BindingsIdList()
         idList.forEach { try? list.add($0) }
 
@@ -40,16 +38,16 @@ extension BindingsUserDiscovery: UserDiscoveryInterface {
 
             guard let contacts = contactList else { return }
             let count = contacts.len()
-            var results = [LookupResult]()
+            var results = [Contact]()
 
             for index in 0..<count {
                 guard let contact = try? contacts.get(index),
                       let marshal = try? contact.marshal(),
-                      let username = try? self.retrieve(from: marshal, fact: .username) else {
+                      ((try? self.retrieve(from: marshal, fact: .username) != nil) != nil) else {
                     log(string: "Skipping", type: .error); continue
                 }
 
-                results.append(.init(id: contact.getID()!, username: username))
+                results.append(Contact(with: contact, status: .stranger))
             }
 
             completion(.success(results))
@@ -103,7 +101,7 @@ extension BindingsUserDiscovery: UserDiscoveryInterface {
         let confirmationId = addFact(bindingsFact?.stringify(), error: &otherError)
 
         if let otherError = otherError {
-            completion(.failure(otherError.friendly()))
+            completion(.failure(otherError))
             return
         }
 
diff --git a/Sources/Integration/Interfaces/BindingsInterface.swift b/Sources/Integration/Interfaces/BindingsInterface.swift
index 9d60f2cbe28876c8fc2dc15403b825e5de1c2180..e9d913af958664feb10e0746f0687dcf17dd4064 100644
--- a/Sources/Integration/Interfaces/BindingsInterface.swift
+++ b/Sources/Integration/Interfaces/BindingsInterface.swift
@@ -1,5 +1,6 @@
 import Models
 import Foundation
+import Combine
 
 public enum MessageDeliveryStatus {
     case sent
@@ -7,9 +8,13 @@ public enum MessageDeliveryStatus {
     case timedout
 }
 
+public typealias DeliveryResult = (Data?, Bool, Bool, Data?)
+
 public typealias BackendEvent = (Int, String?, String?, String?)
 
-public typealias DeliveryResult = (Data?, Bool, Bool, Data?)
+public typealias ClientNew = (String?, String?, Data?, String?, NSErrorPointer) -> Bool
+
+public typealias ClientFromBackup = (String?, String?, Data?, Data?, Data?, NSErrorPointer) -> Data?
 
 public typealias NotificationEvaluation = (String?, String?, NSErrorPointer) -> NotificationManyReportProtocol?
 
@@ -20,6 +25,20 @@ public protocol E2ESendReportType {
     var roundURL: String { get }
 }
 
+public protocol BackupInterface {
+    func stop() throws
+    func addJson(_: String?)
+}
+
+public protocol RestoreReportType {
+    func lenFailed() -> Int
+    func lenRestored() -> Int
+    func getErrorAt(_: Int) -> String
+    func getFailedAt(_: Int) -> Data?
+    func getRestoreContactsError() -> String
+    func getRestoredAt(_: Int) -> Data?
+}
+
 public protocol BindingsInterface {
 
     // MARK: Properties
@@ -48,7 +67,9 @@ public protocol BindingsInterface {
 
     static var login: (String?, Data?, String?, NSErrorPointer) -> BindingsInterface? { get }
 
-    static var newClient: (String?, String?, Data?, String?, NSErrorPointer) -> Bool { get }
+    static var new: ClientNew { get }
+
+    static var fromBackup: ClientFromBackup { get }
 
     static func updateNDF(for: NetworkEnvironment, _: @escaping (Result<Data?, Error>) -> Void)
 
@@ -74,6 +95,8 @@ public protocol BindingsInterface {
 
     func compress(image: Data, _: @escaping(Result<Data, Error>) -> Void)
 
+    func resetSessionWith(_: Data)
+
     func listen(
         report: Data,
         _: @escaping (Result<MessageDeliveryStatus, Error>) -> Void
@@ -98,6 +121,8 @@ public protocol BindingsInterface {
     
     func generateUD() throws -> UserDiscoveryInterface
 
+    func generateUDFromBackup(email: String?, phone: String?) throws -> UserDiscoveryInterface
+
     // MARK: FileTransfer
 
     func generateTransferManager(
@@ -112,9 +137,12 @@ public protocol BindingsInterface {
 
     func listenMessages(_: @escaping (Message) -> Void) throws
 
+    func listenBackups(_: @escaping (Data) -> Void) -> BackupInterface
+
     func listenRequests(
-        _: @escaping (Contact) -> Void,
-        confirmations: @escaping (Contact) -> Void
+        _ requests: @escaping (Contact) -> Void,
+        _ confirmations: @escaping (Contact) -> Void,
+        _ resets: @escaping (Contact) -> Void
     )
 
     func listenPreImageUpdates()
@@ -127,4 +155,11 @@ public protocol BindingsInterface {
     func listenNetworkUpdates(_: @escaping (Bool) -> Void)
 
     func removeContact(_ data: Data) throws
+
+    func restore(
+        ids: Data,
+        using: UserDiscoveryInterface,
+        lookupCallback: @escaping (Result<Contact, Error>) -> Void,
+        restoreCallback: @escaping (Int, Int, Int, String?) -> Void
+    ) -> RestoreReportType
 }
diff --git a/Sources/Integration/Interfaces/UserDiscoveryInterface.swift b/Sources/Integration/Interfaces/UserDiscoveryInterface.swift
index 116cf5072955f5ba432287c6d0d9dc0724e5785d..07398985fd1a25e500bb9015832d18e07226b57b 100644
--- a/Sources/Integration/Interfaces/UserDiscoveryInterface.swift
+++ b/Sources/Integration/Interfaces/UserDiscoveryInterface.swift
@@ -20,7 +20,7 @@ public protocol UserDiscoveryInterface {
 
     func search(fact: String, _: @escaping (Result<Contact, Error>) -> Void) throws
 
-    func lookup(idList: [Data], _: @escaping (Result<[LookupResult], Error>) -> Void)
+    func lookup(idList: [Data], _: @escaping (Result<[Contact], Error>) -> Void)
 
     func register(_: FactType, value: String, _: @escaping (Result<String?, Error>) -> Void)
 }
diff --git a/Sources/Integration/Listeners.swift b/Sources/Integration/Listeners.swift
index 32a6291244e598843585003dd24c468cefcbc5f9..cd81bc446f79b0d1e5b0339cae546f9191b58467 100644
--- a/Sources/Integration/Listeners.swift
+++ b/Sources/Integration/Listeners.swift
@@ -3,6 +3,8 @@ import Shared
 import Bindings
 import Foundation
 
+import Combine
+
 public extension BindingsClient {
     static func listenLogs() {
         let callback = LogCallback { log(string: $0 ?? "", type: .bindings) }
@@ -20,6 +22,12 @@ public extension BindingsClient {
         registerPreimageCallback(receptionId, pin: callback)
     }
 
+    func listenBackups(_ callback: @escaping (Data) -> Void) -> BackupInterface {
+        var error: NSError?
+        let backup = BindingsInitializeBackup("", UpdateBackupCallback(callback), self, &error)
+        return backup!
+    }
+
     func listenMessages(_ callback: @escaping (Message) -> Void) throws {
         let zeroBytes = [UInt8](repeating: 0, count: 33)
 
@@ -34,11 +42,13 @@ public extension BindingsClient {
 
     func listenRequests(
         _ requests: @escaping (Contact) -> Void,
-        confirmations: @escaping (Contact) -> Void
+        _ confirmations: @escaping (Contact) -> Void,
+        _ resets: @escaping (Contact) -> Void
     ) {
-        let requestCallback = RequestCallback { requests(Contact(with: $0, status: .verificationInProgress)) }
+        let resetCallback = ResetCallback { resets(Contact(with: $0, status: .friend)) }
         let confirmCallback = ConfirmationCallback { confirmations(Contact(with: $0, status: .friend)) }
-        registerAuthCallbacks(requestCallback, confirm: confirmCallback, reset: nil)
+        let requestCallback = RequestCallback { requests(Contact(with: $0, status: .verificationInProgress)) }
+        registerAuthCallbacks(requestCallback, confirm: confirmCallback, reset: resetCallback)
     }
 
     func listenNetworkUpdates(_ callback: @escaping (Bool) -> Void) {
@@ -49,7 +59,7 @@ public extension BindingsClient {
         do {
             try registerEventCallback("EventListener", myObj: EventCallback(completion))
         } catch {
-            log(string: "Event listener failed: \(error.localizedDescription)", type: .error)
+            log(string: ">>> Event listener failed: \(error.localizedDescription)", type: .error)
         }
     }
 
@@ -79,7 +89,7 @@ public extension BindingsClient {
                     try roundList.get(index, ret0_: &integer)
                     roundIds.append(integer)
                 } catch {
-                    log(string: "Error inspecting round list:\n\(error.localizedDescription)", type: .error)
+                    log(string: ">>> Error inspecting round list:\n\(error.localizedDescription)", type: .error)
                 }
             }
         }
diff --git a/Sources/Integration/Logging.swift b/Sources/Integration/Logging.swift
index edc3692e5cb9b6f47d81875ec94008221b37043f..c37e60b32c1261112930efe2a84248a74290fe3b 100644
--- a/Sources/Integration/Logging.swift
+++ b/Sources/Integration/Logging.swift
@@ -21,7 +21,7 @@ final class BindingsError: NSObject, BindingsClientErrorProtocol {
 
 extension Error {
     func friendly() -> NSError {
-        log(string: "Switching to friendly error from: \(localizedDescription)", type: .error)
+        log(string: ">>> Switching to friendly error from: \(localizedDescription)", type: .error)
         
         let error = BindingsErrorStringToUserFriendlyMessage(localizedDescription)
         if error.hasPrefix("UR") {
diff --git a/Sources/Integration/Mocks/BindingsMock.swift b/Sources/Integration/Mocks/BindingsMock.swift
index cf1800b0c722925c25db1f2674df0ca527258be9..9d2c8acc3a34b210567bb90cdc9dcbb797d8f14d 100644
--- a/Sources/Integration/Mocks/BindingsMock.swift
+++ b/Sources/Integration/Mocks/BindingsMock.swift
@@ -35,12 +35,11 @@ public final class BindingsMock: BindingsInterface {
 
     public static let version: String = "MOCK"
 
-    public static var login: (String?, Data?, String?, NSErrorPointer) -> BindingsInterface? = {
-        _,_,_,_ in BindingsMock()
-    }
-    public static var newClient: (String?, String?, Data?, String?, NSErrorPointer) -> Bool = {
-        _,_,_,_,_ in true
-    }
+    public static var new: ClientNew = { _,_,_,_,_ in true }
+
+    public static var fromBackup: ClientFromBackup = { _,_,_,_,_,_ in Data() }
+
+    public static var login: (String?, Data?, String?, NSErrorPointer) -> BindingsInterface? = { _,_,_,_ in BindingsMock() }
 
     public func meMarshalled(_: String, email: String?, phone: String?) -> Data {
         meMarshalled
@@ -70,6 +69,11 @@ public final class BindingsMock: BindingsInterface {
 
     public func generateUD() throws -> UserDiscoveryInterface { UserDiscoveryMock() }
 
+    public func generateUDFromBackup(
+        email: String?,
+        phone: String?
+    ) throws -> UserDiscoveryInterface { UserDiscoveryMock() }
+
     public func generateTransferManager(
         _: @escaping (Data, String?, String?, Data?) -> Void
     ) throws -> TransferManagerInterface {
@@ -80,6 +84,8 @@ public final class BindingsMock: BindingsInterface {
 
     public func listenMessages(_: @escaping (Message) -> Void) throws {}
 
+    public func listenBackups(_: @escaping (Data) -> Void) -> BackupInterface { fatalError() }
+
     public func listenNetworkUpdates(_: @escaping (Bool) -> Void) {}
 
     public func confirm(_: Data, _ completion: @escaping (Result<Bool, Error>) -> Void) {
@@ -129,9 +135,12 @@ public final class BindingsMock: BindingsInterface {
 
     public func removeContact(_ data: Data) throws {}
 
+    public func resetSessionWith(_: Data) {}
+
     public func listenRequests(
         _ requests: @escaping (Contact) -> Void,
-        confirmations: @escaping (Contact) -> Void
+        _ confirmations: @escaping (Contact) -> Void,
+        _ resets: @escaping (Contact) -> Void
     ) {
         requestsSubject.sink(receiveValue: requests).store(in: &cancellables)
         confirmationsSubject.sink(receiveValue: confirmations).store(in: &cancellables)
@@ -144,6 +153,15 @@ public final class BindingsMock: BindingsInterface {
         GroupManagerMock()
     }
 
+    public func restore(
+        ids: Data,
+        using ud: UserDiscoveryInterface,
+        lookupCallback: @escaping (Result<Contact, Error>) -> Void,
+        restoreCallback: @escaping (Int, Int, Int, String?) -> Void
+    ) -> RestoreReportType {
+        fatalError()
+    }
+
     public static func updateNDF(for: NetworkEnvironment, _ completion: @escaping (Result<Data?, Error>) -> Void) {
         completion(.success(Data()))
     }
diff --git a/Sources/Integration/Mocks/UserDiscoveryMock.swift b/Sources/Integration/Mocks/UserDiscoveryMock.swift
index b23a612e07ff3f7dde71b68e50b417496155c6e1..69cd094e0eae4ed5c281d5cc3058ad7e94945dec 100644
--- a/Sources/Integration/Mocks/UserDiscoveryMock.swift
+++ b/Sources/Integration/Mocks/UserDiscoveryMock.swift
@@ -9,7 +9,7 @@ final class UserDiscoveryMock: UserDiscoveryInterface {
 
     func confirm(code: String, id: String) throws {}
 
-    func lookup(idList: [Data], _: @escaping (Result<[LookupResult], Error>) -> Void) {}
+    func lookup(idList: [Data], _: @escaping (Result<[Contact], Error>) -> Void) {}
 
     func retrieve(from: Data, fact: FactType) throws -> String? { fact.description }
 
@@ -21,5 +21,22 @@ final class UserDiscoveryMock: UserDiscoveryInterface {
         completion(.success("#CONFIRMATION_CODE_FOR \(value)"))
     }
 
-    func lookup(forUserId: Data, _: @escaping (Result<Contact, Error>) -> Void) {}
+    func lookup(
+        forUserId: Data,
+        _ completion: @escaping (Result<Contact, Error>) -> Void
+    ) {
+        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
+            completion(.success(.init(
+                photo: nil,
+                userId: "mock_username".data(using: .utf8)!,
+                email: nil,
+                phone: nil,
+                status: .stranger,
+                marshaled: "mock_username".data(using: .utf8)!,
+                username: "mock_username",
+                nickname: "mock_nickname",
+                createdAt: Date()
+            )))
+        }
+    }
 }
diff --git a/Sources/Integration/Session/Session+Contacts.swift b/Sources/Integration/Session/Session+Contacts.swift
index 1fbde8f3906abfc7a23e65c8ec4cb5ec679628f7..3a8b20a3f35d4cd211e7f01612a3d14e18976f3d 100644
--- a/Sources/Integration/Session/Session+Contacts.swift
+++ b/Sources/Integration/Session/Session+Contacts.swift
@@ -162,8 +162,6 @@ extension Session {
                     contactToOperate.status = success ? .requested : .requestFailed
                     contactToOperate = try self.dbManager.save(contactToOperate)
 
-
-
                     log(string: "Successfully added \(title)", type: .info)
                 case .failure(let error):
                     contactToOperate.status = .requestFailed
diff --git a/Sources/Integration/Session/Session+Group.swift b/Sources/Integration/Session/Session+Group.swift
index 4abeefe4041bd9aa3358533570959350df469b07..115bd669c39af44a9a17e63a3e0a49313787bfca 100644
--- a/Sources/Integration/Session/Session+Group.swift
+++ b/Sources/Integration/Session/Session+Group.swift
@@ -169,9 +169,9 @@ extension Session {
 
             ud.lookup(idList: ids) {
                 switch $0 {
-                case .success(let result):
+                case .success(let contacts):
                     strangers.forEach { stranger in
-                        if let found = result.first(where: { lookup in lookup.id == stranger.userId }) {
+                        if let found = contacts.first(where: { contact in contact.userId == stranger.userId }) {
                             var updatedStranger = stranger
                             updatedStranger.username = found.username
                             updatedStrangers.append(updatedStranger)
@@ -180,7 +180,6 @@ extension Session {
 
                     DispatchQueue.main.async {
                         updatedStrangers.forEach {
-
                             do {
                                 try self.dbManager.save($0)
                             } catch {
diff --git a/Sources/Integration/Session/Session+UD.swift b/Sources/Integration/Session/Session+UD.swift
index 4046dde176002267e1df84101de6f31dd00b423a..82c1e427b6312ae5ab647e3fb2783388ba4368d6 100644
--- a/Sources/Integration/Session/Session+UD.swift
+++ b/Sources/Integration/Session/Session+UD.swift
@@ -61,5 +61,7 @@ extension Session {
         } else {
             phone = confirmation.content
         }
+
+        updateFactsOnBackup()
     }
 }
diff --git a/Sources/Integration/Session/Session.swift b/Sources/Integration/Session/Session.swift
index 8de6347227c5f8c0acb92b1e32d06a67dda8b1ed..5cdb481b0040f90c52210d27e92bf8ffb7e66d4a 100644
--- a/Sources/Integration/Session/Session.swift
+++ b/Sources/Integration/Session/Session.swift
@@ -5,9 +5,26 @@ import Combine
 import Defaults
 import Database
 import Foundation
+import BackupFeature
 import NetworkMonitor
 import DependencyInjection
 
+struct BackupParameters: Codable {
+    var email: String?
+    var phone: String?
+    var username: String
+}
+
+struct BackupReport: Codable {
+    var contactIds: [String]
+    var parameters: String
+
+    private enum CodingKeys: String, CodingKey {
+        case parameters = "Params"
+        case contactIds = "RestoredContacts"
+    }
+}
+
 public final class Session: SessionType {
     @KeyObject(.theme, defaultValue: nil) var theme: String?
     @KeyObject(.email, defaultValue: nil) var email: String?
@@ -25,6 +42,7 @@ public final class Session: SessionType {
     @KeyObject(.pushNotifications, defaultValue: false) var pushNotifications: Bool
     @KeyObject(.inappnotifications, defaultValue: true) var inappnotifications: Bool
 
+    @Dependency var backupService: BackupService
     @Dependency var networkMonitor: NetworkMonitoring
 
     public let client: Client
@@ -97,11 +115,37 @@ public final class Session: SessionType {
             .eraseToAnyPublisher()
     }
 
+    public init(backupFile: Data, ndf: String) throws {
+        let network = try! DependencyInjection.Container.shared.resolve() as XXNetworking
+        let (client, backupData) = try network.newClientFromBackup(data: backupFile, ndf: ndf)
+        self.client = client
+        dbManager = GRDBDatabaseManager()
+
+        let report = try! JSONDecoder().decode(BackupReport.self, from: backupData!)
+
+        if !report.parameters.isEmpty {
+            let params = try! JSONDecoder().decode(BackupParameters.self, from: Data(report.parameters.utf8))
+
+            username = params.username
+            phone = params.phone
+            email = params.email
+        }
+
+        try continueInitialization()
+
+        if !report.contactIds.isEmpty {
+            client.restoreContacts(fromBackup: try! JSONSerialization.data(withJSONObject: report.contactIds))
+        }
+    }
+
     public init(ndf: String) throws {
         let network = try! DependencyInjection.Container.shared.resolve() as XXNetworking
         self.client = try network.newClient(ndf: ndf)
-
         dbManager = GRDBDatabaseManager()
+        try continueInitialization()
+    }
+
+    private func continueInitialization() throws {
         try dbManager.setup()
 
         setupBindings()
@@ -118,12 +162,7 @@ public final class Session: SessionType {
             pendingVerificationUsers.forEach {
                 var contact = $0
                 contact.status = .verificationFailed
-
-                do {
-                    _ = try dbManager.save(contact)
-                } catch {
-                    log(string: error.localizedDescription, type: .error)
-                }
+                _ = try? dbManager.save(contact)
             }
         }
     }
@@ -133,42 +172,22 @@ public final class Session: SessionType {
     }
 
     public func deleteMyself() throws {
-        log(string: "Will start deleting account process", type: .crumbs)
-
-        guard let username = username, let ud = client.userDiscovery else {
-            log(string: "Failed deleting account. No username or UD", type: .error)
-            return
-        }
-
-        do {
-            try unregisterNotifications()
-        } catch {
-            log(string: "Failed to unregister for notifications", type: .error)
-        }
+        guard let username = username, let ud = client.userDiscovery else { return }
 
+        try? unregisterNotifications()
         try ud.deleteMyself(username)
-        log(string: "Deleted myself from User Discovery", type: .info)
 
         stop()
-        log(string: "Requested network stop", type: .crumbs)
-
         cleanUp()
     }
 
     private func cleanUp() {
         retry(max: 10, retryStrategy: .delay(seconds: 1)) { [unowned self] in
-            guard self.hasRunningTasks == false else {
-                let string = "Tried to clean up database and defaults but network hasn't stopped yet. Sleeping for a second..."
-                log(string: string, type: .error)
-                throw NSError.create("")
-            }
+            guard self.hasRunningTasks == false else { throw NSError.create("") }
         }.finalCatch { _ in fatalError("Couldn't delete account because network is not stopping") }
 
         dbManager.drop()
-        log(string: "Dropped database", type: .info)
-
         FileManager.xxCleanup()
-        log(string: "Wiped disk", type: .info)
 
         email = nil
         phone = nil
@@ -185,8 +204,6 @@ public final class Session: SessionType {
         icognitoKeyboard = false
         pushNotifications = false
         inappnotifications = true
-
-        log(string: "Wiped defaults", type: .info)
     }
 
     public func forceFailMessages() {
@@ -194,12 +211,7 @@ public final class Session: SessionType {
             pendingE2E.forEach {
                 var message = $0
                 message.status = .failedToSend
-
-                do {
-                    try dbManager.save(message)
-                } catch {
-                    log(string: error.localizedDescription, type: .error)
-                }
+                _ = try? dbManager.save(message)
             }
         }
 
@@ -207,12 +219,7 @@ public final class Session: SessionType {
             pendingGroupMessages.forEach {
                 var message = $0
                 message.status = .failed
-
-                do {
-                    try dbManager.save(message)
-                } catch {
-                    log(string: error.localizedDescription, type: .error)
-                }
+                _ = try? dbManager.save(message)
             }
         }
     }
@@ -220,27 +227,17 @@ public final class Session: SessionType {
     private func registerUnfinishedTransfers() {
         guard let unfinisheds: [Message] = try? dbManager.fetch(.sendingAttachment), !unfinisheds.isEmpty else { return }
 
-        log(string: "There are unfinished transfers from the last session. Re-registering their upload progress", type: .crumbs)
-
         for var message in unfinisheds {
-            guard let tid = message.payload.attachment?.transferId else {
-                log(string: "Impossible to resume a transfer that had no TID", type: .error)
-                return
-            }
+            guard let tid = message.payload.attachment?.transferId else { return }
 
             do {
                 try client.transferManager?.listenUploadFromTransfer(with: tid) { completed, sent, arrived, total, error in
                     if completed {
                         message.status = .sent
                         message.payload.attachment?.progress = 1.0
-                        log(string: "FT Up finished", type: .info)
 
                         if let transfer: FileTransfer = try? self.dbManager.fetch(.withTID(tid)).first {
-                            do {
-                                try self.dbManager.delete(transfer)
-                            } catch {
-                                log(string: error.localizedDescription, type: .error)
-                            }
+                            try? self.dbManager.delete(transfer)
                         }
                     } else {
                         if let error = error {
@@ -249,28 +246,40 @@ public final class Session: SessionType {
                         } else {
                             let progress = Float(arrived)/Float(total)
                             message.payload.attachment?.progress = progress
-                            log(string: "FT Up: \(progress)", type: .crumbs)
                             return
                         }
                     }
 
-                    do {
-                        _ = try self.dbManager.save(message)
-                    } catch {
-                        log(string: error.localizedDescription, type: .error)
-                    }
+                    _ = try? self.dbManager.save(message)
                 }
             } catch {
-                log(string: "An error occurred when trying to register unfinished FT: \(error.localizedDescription). Switching it to 'sent'", type: .error)
                 message.status = .sent
+                _ = try? self.dbManager.save(message)
+            }
+        }
+    }
 
-                do {
-                    _ = try self.dbManager.save(message)
-                } catch {
-                    log(string: error.localizedDescription, type: .error)
-                }
+    func updateFactsOnBackup() {
+        struct BackupParameters: Codable {
+            var email: String?
+            var phone: String?
+            var username: String
+
+            var jsonFormat: String {
+                let data = try! JSONEncoder().encode(self)
+                let json = String(data: data, encoding: .utf8)
+                return json!
             }
         }
+
+        let params = BackupParameters(
+            email: email,
+            phone: phone,
+            username: username!
+        ).jsonFormat
+
+        client.addJson(params)
+        backupService.performBackupIfAutomaticIsEnabled()
     }
 
     private func setupBindings() {
@@ -284,26 +293,51 @@ public final class Session: SessionType {
                 }
 
                 verify(contact: request)
-            }
+            }.store(in: &cancellables)
+
+        client.requestsSent
+            .sink { [unowned self] in _ = try? dbManager.save($0) }
             .store(in: &cancellables)
 
-        client.groupMessages
+        client.backup
+            .throttle(for: .seconds(5), scheduler: DispatchQueue.main, latest: true)
+            .sink { [unowned self] in backupService.updateBackup(data: $0) }
+            .store(in: &cancellables)
+
+        client.resets
             .sink { [unowned self] in
-                do {
-                    _ = try dbManager.save($0)
-                } catch {
-                    log(string: "Failed to save an incoming group message: \(error.localizedDescription)", type: .error)
-                }
+                /// This will get called when my contact restore its contact.
+                /// TODO: Hold a record on the chat that this contact restored.
+                ///
+                var contact = $0
+                contact.status = .friend
+                _ = try? dbManager.save(contact)
             }.store(in: &cancellables)
 
-        client.messages
+        backupService.settingsPublisher
+            .map { $0.enabledService != nil }
+            .removeDuplicates()
             .sink { [unowned self] in
-                do {
-                    _ = try dbManager.save($0)
-                } catch {
-                    log(string: "Failed to save an incoming direct message: \(error.localizedDescription)", type: .error)
+                if $0 == true {
+                    client.listenBackup()
+                    updateFactsOnBackup()
+                } else {
+                    client.stopListeningBackup()
                 }
-            }.store(in: &cancellables)
+            }
+            .store(in: &cancellables)
+
+        networkMonitor.statusPublisher
+            .sink { print($0) }
+            .store(in: &cancellables)
+
+        client.groupMessages
+            .sink { [unowned self] in _ = try? dbManager.save($0) }
+            .store(in: &cancellables)
+
+        client.messages
+            .sink { [unowned self] in _ = try? dbManager.save($0) }
+            .store(in: &cancellables)
 
         client.network
             .sink { [unowned self] in networkMonitor.update($0) }
@@ -324,14 +358,9 @@ public final class Session: SessionType {
 
         client.confirmations
             .sink { [unowned self] in
-                guard var contact: Contact = try? dbManager.fetch(.withUserId($0.userId)).first else { return }
-
-                contact.status = .friend
-
-                do {
-                    try dbManager.save(contact)
-                } catch {
-                    log(string: error.localizedDescription, type: .error)
+                if var contact: Contact = try? dbManager.fetch(.withUserId($0.userId)).first {
+                    contact.status = .friend
+                    _ = try? dbManager.save(contact)
                 }
             }.store(in: &cancellables)
     }
diff --git a/Sources/Integration/XXNetwork.swift b/Sources/Integration/XXNetwork.swift
index 26ffe4d36e7c9f131dd42d4f5fe496467467159d..4d6ee69040d61cf04d42f39924fc6d1182960276 100644
--- a/Sources/Integration/XXNetwork.swift
+++ b/Sources/Integration/XXNetwork.swift
@@ -15,18 +15,15 @@ public protocol XXNetworking {
     func purgeFiles()
     func updateErrors()
     func newClient(ndf: String) throws -> Client
-    func loadClient(with: Data) throws -> Client
     func updateNDF(_: @escaping (Result<String, Error>) -> Void)
+    func newClientFromBackup(data: Data, ndf: String) throws -> (Client, Data?)
+    func loadClient(with: Data, fromBackup: Bool, email: String?, phone: String?) throws -> Client
 }
 
 public struct XXNetwork<B: BindingsInterface> {
-    // MARK: Injected
-
     @Dependency private var logger: XXLogger
     @Dependency private var keychain: KeychainHandling
 
-    // MARK: Lifecycle
-
     public init() {}
 }
 
@@ -64,6 +61,30 @@ extension XXNetwork: XXNetworking {
         FileManager.xxCleanup()
     }
 
+    public func newClientFromBackup(data: Data, ndf: String) throws -> (Client, Data?) {
+        var error: NSError?
+
+        let password = B.secret(32)!
+        try keychain.store(password: password)
+
+        let backupData = B.fromBackup(ndf, FileManager.xxPath, password, nil, data, &error)
+        if let error = error { throw error }
+
+        var email: String?
+        var phone: String?
+
+        let report = try! JSONDecoder().decode(BackupReport.self, from: backupData!)
+
+        if !report.parameters.isEmpty {
+            let params = try! JSONDecoder().decode(BackupParameters.self, from: Data(report.parameters.utf8))
+            phone = params.phone
+            email = params.email
+        }
+
+        let client = try loadClient(with: password, fromBackup: true, email: email, phone: phone)
+        return (client, backupData)
+    }
+
     public func newClient(ndf: String) throws -> Client {
         var password: Data!
 
@@ -73,7 +94,7 @@ extension XXNetwork: XXNetworking {
             password = B.secret(32)
             try keychain.store(password: password)
 
-            _ = B.newClient(ndf, FileManager.xxPath, password, nil, &error)
+            _ = B.new(ndf, FileManager.xxPath, password, nil, &error)
             if let error = error { throw error }
         } else {
             guard let secret = try keychain.getPassword() else {
@@ -83,10 +104,15 @@ extension XXNetwork: XXNetworking {
             password = secret
         }
 
-        return try loadClient(with: password)
+        return try loadClient(with: password, fromBackup: false, email: nil, phone: nil)
     }
 
-    public func loadClient(with secret: Data) throws -> Client {
+    public func loadClient(
+        with secret: Data,
+        fromBackup: Bool,
+        email: String?,
+        phone: String?
+    ) throws -> Client {
         var error: NSError?
         let bindings = B.login(FileManager.xxPath, secret, "", &error)
         if let error = error { throw error }
@@ -95,11 +121,10 @@ extension XXNetwork: XXNetworking {
             defaults.set(bindings!.receptionId.base64EncodedString(), forKey: "receptionId")
         }
 
-        return Client(bindings!)
+        return Client(bindings!, fromBackup: fromBackup, email: email, phone: phone)
     }
 }
 
-
 extension NetworkEnvironment {
     var url: String {
         switch self {
diff --git a/Sources/Models/Backup.swift b/Sources/Models/Backup.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a3518af44998b729a0c66f0c347d26d295634898
--- /dev/null
+++ b/Sources/Models/Backup.swift
@@ -0,0 +1,17 @@
+import Foundation
+
+public struct Backup: Equatable, Codable {
+    public var id: String
+    public var date: Date
+    public var size: Float
+
+    public init(
+        id: String,
+        date: Date,
+        size: Float
+    ) {
+        self.id = id
+        self.date = date
+        self.size = size
+    }
+}
diff --git a/Sources/Models/BackupSettings.swift b/Sources/Models/BackupSettings.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1ec2883fc6119882f5008d26b86bb65ed54962c4
--- /dev/null
+++ b/Sources/Models/BackupSettings.swift
@@ -0,0 +1,51 @@
+import Foundation
+
+public struct BackupSettings: Equatable, Codable {
+    public var wifiOnlyBackup: Bool
+    public var automaticBackups: Bool
+    public var enabledService: CloudService?
+    public var connectedServices: Set<CloudService>
+    public var backups: [CloudService: Backup]
+
+    public init(
+        wifiOnlyBackup: Bool = false,
+        automaticBackups: Bool = false,
+        enabledService: CloudService? = nil,
+        connectedServices: Set<CloudService> = [],
+        backups: [CloudService: Backup] = [:]
+    ) {
+        self.wifiOnlyBackup = wifiOnlyBackup
+        self.automaticBackups = automaticBackups
+        self.enabledService = enabledService
+        self.connectedServices = connectedServices
+        self.backups = backups
+    }
+
+    public func toData() -> Data {
+        (try? PropertyListEncoder().encode(self)) ?? Data()
+    }
+
+    public init(fromData data: Data) {
+        let settings = try? PropertyListDecoder().decode(BackupSettings.self, from: data)
+        self.init(
+            wifiOnlyBackup: settings?.wifiOnlyBackup ?? false,
+            automaticBackups: settings?.automaticBackups ?? false,
+            enabledService: settings?.enabledService,
+            connectedServices: settings?.connectedServices ?? [],
+            backups: settings?.backups ?? [:]
+        )
+    }
+}
+
+public struct RestoreSettings {
+    public var backup: Backup?
+    public var cloudService: CloudService
+
+    public init(
+        backup: Backup? = nil,
+        cloudService: CloudService
+    ) {
+        self.backup = backup
+        self.cloudService = cloudService
+    }
+}
diff --git a/Sources/Models/CloudService.swift b/Sources/Models/CloudService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5daba7238d74388dac591b63220dbbc8b1c9f911
--- /dev/null
+++ b/Sources/Models/CloudService.swift
@@ -0,0 +1,5 @@
+public enum CloudService: Equatable, Codable {
+    case drive
+    case icloud
+    case dropbox
+}
diff --git a/Sources/NetworkMonitor/MockNetworkMonitor.swift b/Sources/NetworkMonitor/MockNetworkMonitor.swift
index 46df25e22a2803146ae68827fb2c4e0c14a51cc5..d84355e2a5c90bbdbee48820a7d6a11f51613e0b 100644
--- a/Sources/NetworkMonitor/MockNetworkMonitor.swift
+++ b/Sources/NetworkMonitor/MockNetworkMonitor.swift
@@ -2,11 +2,28 @@ import Combine
 
 public struct MockNetworkMonitor: NetworkMonitoring {
     private let statusRelay = PassthroughSubject<NetworkStatus, Never>()
-    public var statusPublisher: AnyPublisher<NetworkStatus, Never> { statusRelay.eraseToAnyPublisher() }
 
-    public var xxStatus: NetworkStatus { .available }
+    public var connType: AnyPublisher<ConnectionType, Never> {
+        Just(.wifi).eraseToAnyPublisher()
+    }
 
-    public init() {}
-    public func start() {}
-    public func update(_ status: Bool) {}
+    public var statusPublisher: AnyPublisher<NetworkStatus, Never> {
+        statusRelay.eraseToAnyPublisher()
+    }
+
+    public var xxStatus: NetworkStatus {
+        .available
+    }
+
+    public init() {
+        // TODO
+    }
+
+    public func start() {
+        // TODO
+    }
+
+    public func update(_ status: Bool) {
+        // TODO
+    }
 }
diff --git a/Sources/NetworkMonitor/NetworkMonitor.swift b/Sources/NetworkMonitor/NetworkMonitor.swift
index 07effb7889066c2aaf1c57fecf194f41549a6742..ab805ca524e337bc8961c9dac719ac79d74f72f2 100644
--- a/Sources/NetworkMonitor/NetworkMonitor.swift
+++ b/Sources/NetworkMonitor/NetworkMonitor.swift
@@ -2,6 +2,7 @@
 
 import Network
 import Combine
+import Foundation
 
 public enum NetworkStatus: Equatable {
     case unknown
@@ -10,11 +11,19 @@ public enum NetworkStatus: Equatable {
     case internetNotAvailable
 }
 
+public enum ConnectionType {
+    case wifi
+    case ethernet
+    case cellular
+    case unknown
+}
+
 public protocol NetworkMonitoring {
     func start()
     func update(_ status: Bool)
 
     var xxStatus: NetworkStatus { get }
+    var connType: AnyPublisher<ConnectionType, Never> { get }
     var statusPublisher: AnyPublisher<NetworkStatus, Never> { get }
 }
 
@@ -24,11 +33,16 @@ public struct NetworkMonitor: NetworkMonitoring {
     private var monitor = NWPathMonitor()
     private let isXXAvailableRelay = CurrentValueSubject<Bool?, Never>(nil)
     private let isInternetAvailableRelay = CurrentValueSubject<Bool?, Never>(nil)
+    private let connTypeSubject = PassthroughSubject<ConnectionType, Never>()
 
     public var xxStatus: NetworkStatus {
         isXXAvailableRelay.value == true ? .available : .xxNotAvailable
     }
 
+    public var connType: AnyPublisher<ConnectionType, Never> {
+        connTypeSubject.eraseToAnyPublisher()
+    }
+
     public var statusPublisher: AnyPublisher<NetworkStatus, Never> {
         isInternetAvailableRelay.combineLatest(isXXAvailableRelay)
             .map { (isInternetAvailable, isXXAvailable) -> NetworkStatus in
@@ -50,7 +64,8 @@ public struct NetworkMonitor: NetworkMonitoring {
     }
 
     public func start() {
-        monitor.pathUpdateHandler = { [weak isInternetAvailableRelay] in
+        monitor.pathUpdateHandler = { [weak isInternetAvailableRelay, weak connTypeSubject] in
+            connTypeSubject?.send(checkConnectionTypeForPath($0))
             isInternetAvailableRelay?.send($0.status == .satisfied)
         }
 
@@ -60,4 +75,16 @@ public struct NetworkMonitor: NetworkMonitoring {
     public func update(_ status: Bool) {
         isXXAvailableRelay.send(status)
     }
+
+    private func checkConnectionTypeForPath(_ path: NWPath) -> ConnectionType {
+        if path.usesInterfaceType(.wifi) {
+            return .wifi
+        } else if path.usesInterfaceType(.wiredEthernet) {
+            return .ethernet
+        } else if path.usesInterfaceType(.cellular) {
+            return .cellular
+        }
+
+        return .unknown
+    }
 }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift b/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift
index 34b242b905bb20a0c1d4641f52d87dd40a827f1b..3ab0b1633f363bb0f3ba0743357fc79fa83a0f2b 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingEmailConfirmationController.swift
@@ -8,7 +8,7 @@ import DependencyInjection
 import ScrollViewController
 import Models
 
-final class OnboardingEmailConfirmationController: UIViewController {
+public final class OnboardingEmailConfirmationController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -21,7 +21,7 @@ final class OnboardingEmailConfirmationController: UIViewController {
     private var popupCancellables = Set<AnyCancellable>()
     private let viewModel: OnboardingEmailConfirmationViewModel
 
-    init(
+    public init(
         _ confirmation: AttributeConfirmation,
         _ completion: @escaping (UIViewController) -> Void
     ) {
@@ -32,13 +32,13 @@ final class OnboardingEmailConfirmationController: UIViewController {
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         navigationItem.backButtonTitle = " "
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
index a36c5ab29a85ec1afe104b221b8007efe7349685..9997793da6a7803b491ff4a3b0abc57814432a62 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingEmailController.swift
@@ -7,7 +7,7 @@ import Combine
 import DependencyInjection
 import ScrollViewController
 
-final class OnboardingEmailController: UIViewController {
+public final class OnboardingEmailController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -19,13 +19,13 @@ final class OnboardingEmailController: UIViewController {
     private let viewModel = OnboardingEmailViewModel()
     private var popupCancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         navigationItem.backButtonTitle = " "
 
@@ -70,7 +70,13 @@ final class OnboardingEmailController: UIViewController {
             .sink { [unowned self] in
                 viewModel.clearUp()
                 coordinator.toEmailConfirmation(with: $0, from: self) { controller in
-                    coordinator.toSuccess(isEmail: true, from: controller)
+                    let successModel = OnboardingSuccessModel(
+                        title: Localized.Onboarding.Success.Email.title,
+                        subtitle: nil,
+                        nextController: coordinator.toPhone(from:)
+                    )
+
+                    coordinator.toSuccess(with: successModel, from: controller)
                 }
             }.store(in: &cancellables)
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingLaunchController.swift b/Sources/OnboardingFeature/Controllers/OnboardingLaunchController.swift
index 4f061c26757abac89e60ed7acc139ee282afb479..7c384baaee94b9f88bb2402d9f9bccf3f14e459a 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingLaunchController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingLaunchController.swift
@@ -119,7 +119,7 @@ public final class OnboardingLaunchController: UIViewController {
 
                 if let notNowTitle = updateModel.notNowTitle {
                     let notNow = CapsuleButton()
-                    notNow.set(style: .simplestColored, title: notNowTitle)
+                    notNow.set(style: .simplestColoredRed, title: notNowTitle)
 
                     notNow.publisher(for: .touchUpInside)
                         .sink { [unowned self] in
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift
index 1ace1449b88a86e7441cd22650f5be6097129349..c92ee37dd8379cc1bd4fbfb61a790fa35788dd86 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneConfirmationController.swift
@@ -8,7 +8,7 @@ import DependencyInjection
 import ScrollViewController
 import Models
 
-final class OnboardingPhoneConfirmationController: UIViewController {
+public final class OnboardingPhoneConfirmationController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -21,7 +21,7 @@ final class OnboardingPhoneConfirmationController: UIViewController {
     private var popupCancellables = Set<AnyCancellable>()
     private let viewModel: OnboardingPhoneConfirmationViewModel
 
-    init(
+    public init(
         _ confirmation: AttributeConfirmation,
         _ completion: @escaping (UIViewController) -> Void
     ) {
@@ -32,13 +32,13 @@ final class OnboardingPhoneConfirmationController: UIViewController {
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         navigationItem.backButtonTitle = " "
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
index 9f44854db75ef7b8f7bed40e7d0967fe6ae54edf..5fde1a32e78599330fb50a28f558933e13803726 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
@@ -7,7 +7,7 @@ import Combine
 import DependencyInjection
 import ScrollViewController
 
-final class OnboardingPhoneController: UIViewController {
+public final class OnboardingPhoneController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -19,13 +19,13 @@ final class OnboardingPhoneController: UIViewController {
     private let viewModel = OnboardingPhoneViewModel()
     private var popupCancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         navigationItem.backButtonTitle = " "
 
@@ -90,7 +90,13 @@ final class OnboardingPhoneController: UIViewController {
             .sink { [unowned self] in
                 viewModel.clearUp()
                 coordinator.toPhoneConfirmation(with: $0, from: self) { controller in
-                    coordinator.toSuccess(isEmail: false, from: controller)
+                    let successModel = OnboardingSuccessModel(
+                        title: Localized.Onboarding.Success.Phone.title,
+                        subtitle: nil,
+                        nextController: coordinator.toChats(from:)
+                    )
+
+                    coordinator.toSuccess(with: successModel, from: controller)
                 }
             }.store(in: &cancellables)
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift
index 6263e43f4ccdc2c644e37644b81d4ee69d606239..2ed639b116b86ab36b2cdf8924b63626bd653d0c 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingStartController.swift
@@ -5,7 +5,7 @@ import Shared
 import Combine
 import DependencyInjection
 
-final class OnboardingStartController: UIViewController {
+public final class OnboardingStartController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
 
@@ -14,23 +14,23 @@ final class OnboardingStartController: UIViewController {
     private let ndf: String
     private var cancellables = Set<AnyCancellable>()
 
-    override func loadView() {
+    public override func loadView() {
         view = screenView
     }
 
-    init(_ ndf: String) {
+    public init(_ ndf: String) {
         self.ndf = ndf
         super.init(nibName: nil, bundle: nil)
     }
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLayoutSubviews() {
+    public override func viewDidLayoutSubviews() {
         super.viewDidLayoutSubviews()
 
         let gradient = CAGradientLayer()
@@ -48,7 +48,7 @@ final class OnboardingStartController: UIViewController {
         screenView.layer.insertSublayer(gradient, at: 0)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
 
         screenView.startButton.publisher(for: .touchUpInside)
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingSuccessController.swift b/Sources/OnboardingFeature/Controllers/OnboardingSuccessController.swift
index 4ee799a4d4db4dc94e2c6b71cedd65d672157c6e..a260afa9a7c69369f0bf9a9510c851ebe2e20313 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingSuccessController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingSuccessController.swift
@@ -6,31 +6,37 @@ import Combine
 import Countries
 import DependencyInjection
 
-final class OnboardingSuccessController: UIViewController {
+public struct OnboardingSuccessModel {
+    var title: String
+    var subtitle: String?
+    var nextController: (UIViewController) -> Void
+}
+
+public final class OnboardingSuccessController: UIViewController {
     @Dependency private var coordinator: OnboardingCoordinating
 
     lazy private var screenView = OnboardingSuccessView()
-
-    private let isEmail: Bool
     private var cancellables = Set<AnyCancellable>()
 
-    override func loadView() {
+    private var model: OnboardingSuccessModel
+
+    public override func loadView() {
         view = screenView
     }
 
-    init(_ isEmail: Bool) {
-        self.isEmail = isEmail
+    public init(_ model: OnboardingSuccessModel) {
+        self.model = model
         super.init(nibName: nil, bundle: nil)
     }
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLayoutSubviews() {
+    public override func viewDidLayoutSubviews() {
         super.viewDidLayoutSubviews()
 
         let gradient = CAGradientLayer()
@@ -48,23 +54,15 @@ final class OnboardingSuccessController: UIViewController {
         screenView.layer.insertSublayer(gradient, at: 0)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
 
-        let type = isEmail ?
-            Localized.Onboarding.Email.input :
-            Localized.Onboarding.Phone.input
-
-        screenView.set(type: type.components(separatedBy: " ").first!)
+        screenView.setTitle(model.title)
+        screenView.setSubtitle(model.subtitle)
 
         screenView.nextButton
             .publisher(for: .touchUpInside)
-            .sink { [unowned self] in
-                if isEmail {
-                    coordinator.toPhone(from: self)
-                } else {
-                    coordinator.toChats(from: self)
-                }
-            }.store(in: &cancellables)
+            .sink { [unowned self] in model.nextController(self) }
+            .store(in: &cancellables)
     }
 }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
index e43a0a69527de944a2992cf1fd8572cba4eaa525..635cb70388ba061b96c07e0f97553186abad843c 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingUsernameController.swift
@@ -7,7 +7,7 @@ import Combine
 import DependencyInjection
 import ScrollViewController
 
-final class OnboardingUsernameController: UIViewController {
+public final class OnboardingUsernameController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -15,24 +15,26 @@ final class OnboardingUsernameController: UIViewController {
     lazy private var screenView = OnboardingUsernameView()
     lazy private var scrollViewController = ScrollViewController()
 
+    private let ndf: String
     private var cancellables = Set<AnyCancellable>()
     private let viewModel: OnboardingUsernameViewModel!
     private var popupCancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    init(_ ndf: String) {
+    public init(_ ndf: String) {
+        self.ndf = ndf
         self.viewModel = OnboardingUsernameViewModel(ndf: ndf)
         super.init(nibName: nil, bundle: nil)
     }
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupScrollView()
         setupBindings()
@@ -68,6 +70,12 @@ final class OnboardingUsernameController: UIViewController {
             .sink { [unowned self] in viewModel.didInput($0) }
             .store(in: &cancellables)
 
+        screenView.restoreView.restoreButton
+            .publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in coordinator.toRestoreList(with: ndf, from: self) }
+            .store(in: &cancellables)
+
         screenView.inputField.returnPublisher
             .sink { [unowned self] in
                 if screenView.nextButton.isEnabled {
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
index d994fd60815655360919fb4a35df0b888dbd6546..5efe0d7e0aa23e6c228ddb6c1c7443187155b0b6 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
@@ -6,7 +6,7 @@ import Combine
 import Defaults
 import DependencyInjection
 
-final class OnboardingWelcomeController: UIViewController {
+public final class OnboardingWelcomeController: UIViewController {
     @KeyObject(.username, defaultValue: "") var username: String
     @Dependency private var coordinator: OnboardingCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -16,17 +16,17 @@ final class OnboardingWelcomeController: UIViewController {
     private var cancellables = Set<AnyCancellable>()
     private var popupCancellables = Set<AnyCancellable>()
 
-    override func loadView() {
+    public override func loadView() {
         view = screenView
     }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar.customize(translucent: true)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupBindings()
 
diff --git a/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift b/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift
index 485a89d7ec62d51ca0133f94c5d50bdbe82ff70b..0d12a9e0f63c9bbeda543d51a84634ddc133d063 100644
--- a/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift
+++ b/Sources/OnboardingFeature/Coordinator/OnboardingCoordinator.swift
@@ -4,6 +4,8 @@ import Models
 import Countries
 import Presentation
 
+public typealias AttributeControllerClosure = (UIViewController) -> Void
+
 public protocol OnboardingCoordinating {
     func toChats(from: UIViewController)
     func toEmail(from: UIViewController)
@@ -11,9 +13,11 @@ public protocol OnboardingCoordinating {
     func toWelcome(from: UIViewController)
     func toStart(with: String, from: UIViewController)
     func toUsername(with: String, from: UIViewController)
-    func toSuccess(isEmail: Bool, from: UIViewController)
     func toPopup(_: UIViewController, from: UIViewController)
 
+    func toSuccess(with: OnboardingSuccessModel, from: UIViewController)
+    func toRestoreList(with: String, from: UIViewController)
+
     func toEmailConfirmation(
         with: AttributeConfirmation,
         from: UIViewController,
@@ -33,118 +37,114 @@ public protocol OnboardingCoordinating {
 }
 
 public struct OnboardingCoordinator: OnboardingCoordinating {
-    public init(chatListFactory: @escaping () -> UIViewController) {
-        self.chatListFactory = chatListFactory
-    }
-
-    var pusher: Presenting = PushPresenter()
-    var replacer: Presenting = ReplacePresenter()
+    var pushPresenter: Presenting = PushPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
-
-    // MARK: Factories
-
-    var chatListFactory: () -> UIViewController
-
-    var welcomeFactory: () -> UIViewController
-        = OnboardingWelcomeController.init
+    var replacePresenter: Presenting = ReplacePresenter()
 
     var emailFactory: () -> UIViewController
-        = OnboardingEmailController.init
-
     var phoneFactory: () -> UIViewController
-        = OnboardingPhoneController.init
-
+    var welcomeFactory: () -> UIViewController
+    var chatListFactory: () -> UIViewController
     var startFactory: (String) -> UIViewController
-        = OnboardingStartController.init(_:)
-
     var usernameFactory: (String) -> UIViewController
-        = OnboardingUsernameController.init(_:)
-
-    var successFactory: (Bool) -> UIViewController
-        = OnboardingSuccessController.init(_:)
-
+    var restoreListFactory: (String) -> UIViewController
+    var successFactory: (OnboardingSuccessModel) -> UIViewController
     var countriesFactory: (@escaping (Country) -> Void) -> UIViewController
-        = CountryListController.init(_:)
-
-    var phoneConfirmationFactory: (AttributeConfirmation, @escaping (UIViewController) -> Void) -> UIViewController
-        = OnboardingPhoneConfirmationController.init(_:_:)
-
-    var emailConfirmationFactory: (AttributeConfirmation, @escaping (UIViewController) -> Void) -> UIViewController
-        = OnboardingEmailConfirmationController.init(_:_:)
+    var phoneConfirmationFactory: (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController
+    var emailConfirmationFactory: (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController
+
+    public init(
+        emailFactory: @escaping () -> UIViewController,
+        phoneFactory: @escaping () -> UIViewController,
+        welcomeFactory: @escaping () -> UIViewController,
+        chatListFactory: @escaping () -> UIViewController,
+        startFactory: @escaping (String) -> UIViewController,
+        usernameFactory: @escaping (String) -> UIViewController,
+        restoreListFactory: @escaping (String) -> UIViewController,
+        successFactory: @escaping (OnboardingSuccessModel) -> UIViewController,
+        countriesFactory: @escaping (@escaping (Country) -> Void) -> UIViewController,
+        phoneConfirmationFactory: @escaping (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController,
+        emailConfirmationFactory: @escaping (AttributeConfirmation, @escaping AttributeControllerClosure) -> UIViewController
+    ) {
+        self.emailFactory = emailFactory
+        self.phoneFactory = phoneFactory
+        self.startFactory = startFactory
+        self.welcomeFactory = welcomeFactory
+        self.usernameFactory = usernameFactory
+        self.chatListFactory = chatListFactory
+        self.restoreListFactory = restoreListFactory
+        self.successFactory = successFactory
+        self.countriesFactory = countriesFactory
+        self.phoneConfirmationFactory = phoneConfirmationFactory
+        self.emailConfirmationFactory = emailConfirmationFactory
+    }
 }
 
 public extension OnboardingCoordinator {
-    func toSuccess(
-        isEmail: Bool,
-        from parent: UIViewController
-    ) {
-        let screen = successFactory(isEmail)
-        replacer.present(screen, from: parent)
+    func toEmail(from parent: UIViewController) {
+        let screen = emailFactory()
+        replacePresenter.present(screen, from: parent)
     }
 
-    func toEmailConfirmation(
-        with confirmation: AttributeConfirmation,
-        from parent: UIViewController,
-        completion: @escaping (UIViewController) -> Void
-    ) {
-        let screen = emailConfirmationFactory(confirmation, completion)
-        pusher.present(screen, from: parent)
+    func toPhone(from parent: UIViewController) {
+        let screen = phoneFactory()
+        replacePresenter.present(screen, from: parent)
     }
 
-    func toPhoneConfirmation(
-        with confirmation: AttributeConfirmation,
-        from parent: UIViewController,
-        completion: @escaping (UIViewController) -> Void
-    ) {
-        let screen = phoneConfirmationFactory(confirmation, completion)
-        pusher.present(screen, from: parent)
+    func toChats(from parent: UIViewController) {
+        let screen = chatListFactory()
+        replacePresenter.present(screen, from: parent)
     }
 
-    func toEmail(from parent: UIViewController) {
-        let screen = emailFactory()
-        replacer.present(screen, from: parent)
+    func toWelcome(from parent: UIViewController) {
+        let screen = welcomeFactory()
+        replacePresenter.present(screen, from: parent)
     }
 
-    func toPhone(from parent: UIViewController) {
-        let screen = phoneFactory()
-        replacer.present(screen, from: parent)
+    func toRestoreList(with ndf: String, from parent: UIViewController) {
+        let screen = restoreListFactory(ndf)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toCountries(
-        from parent: UIViewController,
-        _ onChoose: @escaping (Country) -> Void
-    ) {
-        let screen = countriesFactory(onChoose)
-        pusher.present(screen, from: parent)
+    func toSuccess(with model: OnboardingSuccessModel, from parent: UIViewController) {
+        let screen = successFactory(model)
+        replacePresenter.present(screen, from: parent)
+    }
+
+    func toStart(with ndf: String, from parent: UIViewController) {
+        let screen = startFactory(ndf)
+        replacePresenter.present(screen, from: parent)
     }
 
     func toUsername(with ndf: String, from parent: UIViewController) {
         let screen = usernameFactory(ndf)
-        replacer.present(screen, from: parent)
+        replacePresenter.present(screen, from: parent)
     }
 
-    func toChats(from parent: UIViewController) {
-        let screen = chatListFactory()
-        replacer.present(screen, from: parent)
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
     }
 
-    func toStart(
-        with ndf: String,
-        from parent: UIViewController
-    ) {
-        let screen = startFactory(ndf)
-        replacer.present(screen, from: parent)
+    func toCountries(from parent: UIViewController, _ onChoose: @escaping (Country) -> Void) {
+        let screen = countriesFactory(onChoose)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
+    func toEmailConfirmation(
+        with confirmation: AttributeConfirmation,
+        from parent: UIViewController,
+        completion: @escaping (UIViewController) -> Void
     ) {
-        bottomPresenter.present(popup, from: parent)
+        let screen = emailConfirmationFactory(confirmation, completion)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toWelcome(from parent: UIViewController) {
-        let screen = welcomeFactory()
-        replacer.present(screen, from: parent)
+    func toPhoneConfirmation(
+        with confirmation: AttributeConfirmation,
+        from parent: UIViewController,
+        completion: @escaping (UIViewController) -> Void
+    ) {
+        let screen = phoneConfirmationFactory(confirmation, completion)
+        pushPresenter.present(screen, from: parent)
     }
 }
diff --git a/Sources/OnboardingFeature/ViewModels/OnboardingLaunchViewModel.swift b/Sources/OnboardingFeature/ViewModels/OnboardingLaunchViewModel.swift
index 34ac05f9a62f6244297a2e40a607be6f8e4cd9e4..9992881ee768ac0010f57b0f506ff62fbc1f48e1 100644
--- a/Sources/OnboardingFeature/ViewModels/OnboardingLaunchViewModel.swift
+++ b/Sources/OnboardingFeature/ViewModels/OnboardingLaunchViewModel.swift
@@ -8,6 +8,7 @@ import Permissions
 import VersionChecking
 import CombineSchedulers
 import DependencyInjection
+import DropboxFeature
 
 struct UpdatePopupModel {
     let body: String
@@ -28,6 +29,7 @@ final class OnboardingLaunchViewModel {
     @Dependency private var network: XXNetworking
     @Dependency private var versioning: VersionChecker
     @Dependency private var permissions: PermissionHandling
+    @Dependency private var dropboxService: DropboxInterface
 
     // MARK: Properties
 
@@ -61,7 +63,7 @@ final class OnboardingLaunchViewModel {
                     updateRelay.send(.init(
                         body: "There is a new version available that enhance the current performance and usability.",
                         updateTitle: "Update",
-                        updateStyle: .simplestColored,
+                        updateStyle: .simplestColoredRed,
                         notNowTitle: "Not now",
                         appUrl: info.appUrl
                     ))
@@ -107,6 +109,7 @@ final class OnboardingLaunchViewModel {
         guard network.hasClient == true else {
             hudRelay.send(.none)
             usernameRelay.send(ndf)
+            dropboxService.unlink()
             return
         }
 
@@ -114,6 +117,7 @@ final class OnboardingLaunchViewModel {
             network.purgeFiles()
             hudRelay.send(.none)
             usernameRelay.send(ndf)
+            dropboxService.unlink()
             return
         }
 
diff --git a/Sources/OnboardingFeature/Views/OnboardingSuccessView.swift b/Sources/OnboardingFeature/Views/OnboardingSuccessView.swift
index 04f0a9bb2117915942c918105a3ad4dfdaa583ad..7e02167c2786cb11c4fcc04d6ad4930b9fab92e2 100644
--- a/Sources/OnboardingFeature/Views/OnboardingSuccessView.swift
+++ b/Sources/OnboardingFeature/Views/OnboardingSuccessView.swift
@@ -4,6 +4,7 @@ import Shared
 final class OnboardingSuccessView: UIView {
     let iconImageView = UIImageView()
     let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
     let nextButton = CapsuleButton()
 
     init() {
@@ -13,8 +14,13 @@ final class OnboardingSuccessView: UIView {
         iconImageView.image = Asset.onboardingSuccess.image
         nextButton.set(style: .white, title: Localized.Onboarding.Success.action)
 
+        subtitleLabel.numberOfLines = 0
+        subtitleLabel.textColor = Asset.neutralWhite.color
+        subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+
         addSubview(iconImageView)
         addSubview(titleLabel)
+        addSubview(subtitleLabel)
         addSubview(nextButton)
 
         iconImageView.snp.makeConstraints { make in
@@ -28,6 +34,12 @@ final class OnboardingSuccessView: UIView {
             make.right.equalToSuperview().offset(-90)
         }
 
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(30)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-90)
+        }
+
         nextButton.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(24)
             make.right.equalToSuperview().offset(-24)
@@ -37,14 +49,12 @@ final class OnboardingSuccessView: UIView {
 
     required init?(coder: NSCoder) { nil }
 
-    func set(type: String) {
+    func setTitle(_ title: String) {
         let paragraph = NSMutableParagraphStyle()
         paragraph.alignment = .left
         paragraph.lineHeightMultiple = 1.1
 
-        let attrString = NSMutableAttributedString(
-            string: Localized.Onboarding.Success.title(type.lowercased())
-        )
+        let attrString = NSMutableAttributedString(string: title)
 
         attrString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 39.0))
         attrString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color)
@@ -58,4 +68,8 @@ final class OnboardingSuccessView: UIView {
         titleLabel.numberOfLines = 0
         titleLabel.attributedText = attrString
     }
+
+    func setSubtitle(_ subtitle: String?) {
+        subtitleLabel.text = subtitle
+    }
 }
diff --git a/Sources/OnboardingFeature/Views/OnboardingUsernameRestoreView.swift b/Sources/OnboardingFeature/Views/OnboardingUsernameRestoreView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f03437558a9157fd6846ce76436f1ed1c4d8822b
--- /dev/null
+++ b/Sources/OnboardingFeature/Views/OnboardingUsernameRestoreView.swift
@@ -0,0 +1,47 @@
+import UIKit
+import Shared
+
+final class OnboardingUsernameRestoreView: UIView {
+    let titleLabel = UILabel()
+    let restoreButton = CapsuleButton()
+    let separatorView = UIView()
+
+    init() {
+        super.init(frame: .zero)
+
+        titleLabel.text = Localized.Onboarding.Username.Restore.title
+        restoreButton.set(style: .seeThrough, title: Localized.Onboarding.Username.Restore.action)
+
+        titleLabel.numberOfLines = 0
+        titleLabel.textAlignment = .center
+        titleLabel.font = Fonts.Mulish.bold.font(size: 24)
+
+        addSubview(titleLabel)
+        addSubview(restoreButton)
+        addSubview(separatorView)
+
+        separatorView.backgroundColor = Asset.neutralLine.color
+
+        separatorView.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview().offset(24)
+            make.right.equalToSuperview().offset(-24)
+            make.height.equalTo(1)
+        }
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(separatorView.snp.bottom).offset(40)
+            make.left.equalToSuperview().offset(20)
+            make.right.equalToSuperview().offset(-20)
+        }
+
+        restoreButton.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(34)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-40)
+            make.bottom.equalToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/OnboardingFeature/Views/OnboardingUsernameView.swift b/Sources/OnboardingFeature/Views/OnboardingUsernameView.swift
index 6ed0744f1186cce25d4fbf7a1106f5d66372e5a6..32ec1e43e312aee7880f1003c9e3f4ec0f18e899 100644
--- a/Sources/OnboardingFeature/Views/OnboardingUsernameView.swift
+++ b/Sources/OnboardingFeature/Views/OnboardingUsernameView.swift
@@ -7,6 +7,7 @@ final class OnboardingUsernameView: UIView {
     let subtitleView = TextWithInfoView()
     let inputField = InputField()
     let nextButton = CapsuleButton()
+    let restoreView = OnboardingUsernameRestoreView()
 
     var didTapInfo: (() -> Void)?
 
@@ -31,6 +32,7 @@ final class OnboardingUsernameView: UIView {
         addSubview(subtitleView)
         addSubview(inputField)
         addSubview(nextButton)
+        addSubview(restoreView)
 
         titleLabel.snp.makeConstraints { make in
             make.top.equalToSuperview().offset(30)
@@ -54,6 +56,12 @@ final class OnboardingUsernameView: UIView {
             make.top.greaterThanOrEqualTo(inputField.snp.bottom).offset(20)
             make.left.equalToSuperview().offset(40)
             make.right.equalToSuperview().offset(-40)
+        }
+
+        restoreView.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualTo(nextButton.snp.bottom).offset(30)
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
             make.bottom.equalTo(safeAreaLayoutGuide).offset(-50)
         }
     }
diff --git a/Sources/Popup/StackItems/PopupButton.swift b/Sources/Popup/StackItems/PopupButton.swift
index cecdcc3547b015ee126cf4410c1dd5ac7ff6687c..0715ceef3985ff1f11fd524996bfb00338dbfbd9 100644
--- a/Sources/Popup/StackItems/PopupButton.swift
+++ b/Sources/Popup/StackItems/PopupButton.swift
@@ -3,8 +3,6 @@ import Shared
 import Combine
 
 public final class PopupButton: PopupStackItem {
-    // MARK: Properties
-
     let font: UIFont?
     let title: String?
     let color: UIColor?
@@ -18,8 +16,6 @@ public final class PopupButton: PopupStackItem {
 
     public var action: AnyPublisher<Void, Never> { actionSubject.eraseToAnyPublisher() }
 
-    // MARK: Lifecycle
-
     public init(
         title: String? = nil,
         font: UIFont? = Fonts.Mulish.regular.font(size: 12.0),
@@ -36,8 +32,6 @@ public final class PopupButton: PopupStackItem {
         self.accessibility = accessibility
     }
 
-    // MARK: Builder
-
     public func makeView() -> UIView {
         cancellables.removeAll()
 
@@ -59,3 +53,72 @@ public final class PopupButton: PopupStackItem {
         }
     }
 }
+
+public final class PopupRadioButton: PopupStackItem {
+    let radioView = UIView()
+    let titleLabel = UILabel()
+    let radioInnerView = UIView()
+
+    public var spacingAfter: CGFloat? = 10
+    private var cancellables = Set<AnyCancellable>()
+    private let actionSubject = PassthroughSubject<Void, Never>()
+
+    public var action: AnyPublisher<Void, Never> { actionSubject.eraseToAnyPublisher() }
+
+    public init(
+        title: String,
+        isSelected: Bool
+    ) {
+        titleLabel.text = title
+        titleLabel.textColor = Asset.neutralDark.color
+        titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0)
+
+        radioView.layer.cornerRadius = 11.0
+        radioInnerView.layer.cornerRadius = 3
+        radioView.isUserInteractionEnabled = false
+
+        if isSelected {
+            radioView.layer.borderWidth = 0.0
+            radioView.backgroundColor = Asset.brandLight.color
+            radioView.layer.borderColor = Asset.brandLight.color.cgColor
+            radioInnerView.backgroundColor = Asset.neutralWhite.color
+        } else {
+            radioView.layer.borderWidth = 1.0
+            radioView.backgroundColor = Asset.neutralSecondary.color
+            radioView.layer.borderColor = Asset.neutralLine.color.cgColor
+            radioInnerView.backgroundColor = .clear
+        }
+    }
+
+    public func makeView() -> UIView {
+        cancellables.removeAll()
+
+        let view = UIControl()
+        view.addSubview(titleLabel)
+        view.addSubview(radioView)
+        radioView.addSubview(radioInnerView)
+
+        titleLabel.snp.makeConstraints { make in
+            make.left.equalToSuperview().offset(42)
+            make.centerY.equalToSuperview()
+        }
+
+        radioView.snp.makeConstraints { make in
+            make.right.equalTo(titleLabel.snp.left).offset(-12)
+            make.width.height.equalTo(20)
+            make.centerY.equalToSuperview()
+            make.bottom.equalToSuperview().offset(-5)
+        }
+
+        radioInnerView.snp.makeConstraints { make in
+            make.width.height.equalTo(6)
+            make.center.equalToSuperview()
+        }
+
+        view.publisher(for: .touchUpInside)
+            .sink { [weak self] in self?.actionSubject.send() }
+            .store(in: &cancellables)
+
+        return view
+    }
+}
diff --git a/Sources/Popup/StackItems/PopupEmptyView.swift b/Sources/Popup/StackItems/PopupEmptyView.swift
index 96d4031450fb0a51feaca3f466365fb9a8488612..bc32b728c6a5cc3e9bc723465f8487036786aafa 100644
--- a/Sources/Popup/StackItems/PopupEmptyView.swift
+++ b/Sources/Popup/StackItems/PopupEmptyView.swift
@@ -1,11 +1,16 @@
 import UIKit
 
 public final class PopupEmptyView: PopupStackItem {
-    // MARK: Lifecycle
+    private var height: CGFloat
 
-    public init() {}
+    public init(height: CGFloat) {
+        self.height = height
+    }
 
-    // MARK: Builder
+    public func makeView() -> UIView {
+        let view = UIView()
+        view.snp.makeConstraints { $0.height.equalTo(height) }
 
-    public func makeView() -> UIView { UIView() }
+        return view
+    }
 }
diff --git a/Sources/Popup/StackItems/PopupStackView.swift b/Sources/Popup/StackItems/PopupStackView.swift
index 56271d01ccef4b43eab59023185d73d901151219..a705e8e284ee4a50514270d0c910204b1defa572 100644
--- a/Sources/Popup/StackItems/PopupStackView.swift
+++ b/Sources/Popup/StackItems/PopupStackView.swift
@@ -2,8 +2,6 @@ import UIKit
 import Shared
 
 public final class PopupStackView: PopupStackItem {
-    // MARK: Properties
-    
     let views: [UIView]
     let spacing: CGFloat
     let axis: NSLayoutConstraint.Axis
@@ -11,8 +9,6 @@ public final class PopupStackView: PopupStackItem {
     
     public var spacingAfter: CGFloat? = 10
     
-    // MARK: Lifecycle
-    
     public init(
         axis: NSLayoutConstraint.Axis = .horizontal,
         spacing: CGFloat = 10,
@@ -25,8 +21,6 @@ public final class PopupStackView: PopupStackItem {
         self.distribution = distribution
     }
     
-    // MARK: Builder
-    
     public func makeView() -> UIView {
         let stack = UIStackView()
         stack.axis = axis
diff --git a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift
index 5cbaff29877d60b4bec21a31dd93bef12e6a855b..d909662aa961fda594217d71d4dd940bd0cf80af 100644
--- a/Sources/ProfileFeature/Controllers/ProfileCodeController.swift
+++ b/Sources/ProfileFeature/Controllers/ProfileCodeController.swift
@@ -9,7 +9,7 @@ import ScrollViewController
 
 public typealias ControllerClosure = (UIViewController, AttributeConfirmation) -> Void
 
-final class ProfileCodeController: UIViewController {
+public final class ProfileCodeController: UIViewController {
     @Dependency private var hud: HUDType
 
     lazy private var screenView = ProfileCodeView()
@@ -20,13 +20,16 @@ final class ProfileCodeController: UIViewController {
     private var cancellables = Set<AnyCancellable>()
     lazy private var viewModel = ProfileCodeViewModel(confirmation)
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar
             .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    init(_ confirmation: AttributeConfirmation, _ completion: @escaping ControllerClosure) {
+    public init(
+        _ confirmation: AttributeConfirmation,
+        _ completion: @escaping ControllerClosure
+    ) {
         self.completion = completion
         self.confirmation = confirmation
         super.init(nibName: nil, bundle: nil)
@@ -34,9 +37,8 @@ final class ProfileCodeController: UIViewController {
 
     required init?(coder: NSCoder) { nil }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
-
         setupNavigationBar()
         setupScrollView()
         setupBindings()
diff --git a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift
index f5fb58c57450d93ae2b3ad8150964ed1e61c24ae..fb85221449bea9ecd89b1a9507db4182b7365622 100644
--- a/Sources/ProfileFeature/Controllers/ProfileEmailController.swift
+++ b/Sources/ProfileFeature/Controllers/ProfileEmailController.swift
@@ -6,7 +6,7 @@ import Theme
 import DependencyInjection
 import ScrollViewController
 
-final class ProfileEmailController: UIViewController {
+public final class ProfileEmailController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: ProfileCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -17,14 +17,14 @@ final class ProfileEmailController: UIViewController {
     private let viewModel = ProfileEmailViewModel()
     private var cancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar
             .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupNavigationBar()
         setupScrollView()
diff --git a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift
index 5d2a91eab29a6354e6367d7d03e3747564603c3b..b6ddaffb5574bdb44f70994fe2ce2c7b0e98b3c0 100644
--- a/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift
+++ b/Sources/ProfileFeature/Controllers/ProfilePhoneController.swift
@@ -8,7 +8,7 @@ import ScrollViewController
 
 #warning("TODO: Merge ProfilePhoneController/ProfileEmailController")
 
-final class ProfilePhoneController: UIViewController {
+public final class ProfilePhoneController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var coordinator: ProfileCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -19,14 +19,14 @@ final class ProfilePhoneController: UIViewController {
     private let viewModel = ProfilePhoneViewModel()
     private var cancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         statusBarController.style.send(.darkContent)
         navigationController?.navigationBar
         .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupNavigationBar()
         setupScrollView()
@@ -108,8 +108,6 @@ final class ProfilePhoneController: UIViewController {
             .store(in: &cancellables)
     }
 
-    // MARK: ObjC
-
     @objc private func didTapBack() {
         navigationController?.popViewController(animated: true)
     }
diff --git a/Sources/ProfileFeature/Coordinator/ProfileCoordinator.swift b/Sources/ProfileFeature/Coordinator/ProfileCoordinator.swift
index ff2615f2dd8db88395d86c667d23691b774f4085..d3eee0e16d403bb07d590a950c3fe50dba3f1bf5 100644
--- a/Sources/ProfileFeature/Coordinator/ProfileCoordinator.swift
+++ b/Sources/ProfileFeature/Coordinator/ProfileCoordinator.swift
@@ -25,61 +25,43 @@ public protocol ProfileCoordinating {
 }
 
 public struct ProfileCoordinator: ProfileCoordinating {
-    public init() {}
-
-    var pusher: Presenting = PushPresenter()
-    var presenter: Presenting = ModalPresenter()
+    var pushPresenter: Presenting = PushPresenter()
+    var modalPresenter: Presenting = ModalPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
 
-    // MARK: Factories
-
     var emailFactory: () -> UIViewController
-        = ProfileEmailController.init
-
     var phoneFactory: () -> UIViewController
-        = ProfilePhoneController.init
-
     var imagePickerFactory: () -> UIImagePickerController
-        = UIImagePickerController.init
-
-    var permissionFactory: () -> RequestPermissionController = RequestPermissionController.init
-
+    var permissionFactory: () -> RequestPermissionController
     var countriesFactory: (@escaping (Country) -> Void) -> UIViewController
-        = CountryListController.init(_:)
-
     var codeFactory: (AttributeConfirmation, @escaping ControllerClosure) -> UIViewController
-        = ProfileCodeController.init(_:_:)
-}
-
-public extension ProfileCoordinator {
-    func toPermission(type: PermissionType, from parent: UIViewController) {
-        let screen = permissionFactory()
-        screen.setup(type: type)
-        pusher.present(screen, from: parent)
-    }
 
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
+    public init(
+        emailFactory: @escaping () -> UIViewController,
+        phoneFactory: @escaping () -> UIViewController,
+        imagePickerFactory: @escaping () -> UIImagePickerController,
+        permissionFactory: @escaping () -> RequestPermissionController, // ⚠️
+        countriesFactory: @escaping (@escaping (Country) -> Void) -> UIViewController,
+        codeFactory: @escaping (AttributeConfirmation, @escaping ControllerClosure) -> UIViewController
     ) {
-        bottomPresenter.present(popup, from: parent)
-    }
-
-    func toPhotos(from parent: UIViewController) {
-        let screen = imagePickerFactory()
-        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
-        screen.allowsEditing = true
-        presenter.present(screen, from: parent)
+        self.codeFactory = codeFactory
+        self.emailFactory = emailFactory
+        self.phoneFactory = phoneFactory
+        self.countriesFactory = countriesFactory
+        self.permissionFactory = permissionFactory
+        self.imagePickerFactory = imagePickerFactory
     }
+}
 
+public extension ProfileCoordinator {
     func toEmail(from parent: UIViewController) {
         let screen = emailFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toPhone(from parent: UIViewController) {
         let screen = phoneFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toCode(
@@ -88,14 +70,28 @@ public extension ProfileCoordinator {
         _ completion: @escaping ControllerClosure
     ) {
         let screen = codeFactory(confirmation, completion)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toCountries(
-        from parent: UIViewController,
-        _ onChoose: @escaping (Country) -> Void
-    ) {
+    func toPermission(type: PermissionType, from parent: UIViewController) {
+        let screen = permissionFactory()
+        screen.setup(type: type)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
+    }
+
+    func toCountries(from parent: UIViewController, _ onChoose: @escaping (Country) -> Void) {
         let screen = countriesFactory(onChoose)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toPhotos(from parent: UIViewController) {
+        let screen = imagePickerFactory()
+        screen.delegate = (parent as? (UIImagePickerControllerDelegate & UINavigationControllerDelegate))
+        screen.allowsEditing = true
+        modalPresenter.present(screen, from: parent)
     }
 }
diff --git a/Sources/RequestsFeature/Controllers/VerifyingController.swift b/Sources/RequestsFeature/Controllers/VerifyingController.swift
index cc154f62b1ac21348f5db835f6f87ecc003dbddd..65d20075b80e55aa309d86fa9e59f22524e52cef 100644
--- a/Sources/RequestsFeature/Controllers/VerifyingController.swift
+++ b/Sources/RequestsFeature/Controllers/VerifyingController.swift
@@ -1,16 +1,16 @@
 import UIKit
 import Combine
 
-final class VerifyingController: UIViewController {
+public final class VerifyingController: UIViewController {
     lazy private var screenView = VerifyingView()
 
     private var cancellables = Set<AnyCancellable>()
 
-    override func loadView() {
+    public override func loadView() {
         view = screenView
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         screenView.action.publisher(for: .touchUpInside)
             .receive(on: DispatchQueue.main)
diff --git a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
index f48524969d5ef640c751bb88fae1241a72a17012..2ef069e020f5f36c25b51699596dd89966181cf0 100644
--- a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
+++ b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
@@ -6,46 +6,37 @@ import ContactFeature
 
 public protocol RequestsCoordinating {
     func toSearch(from: UIViewController)
+    func toVerifying(from: UIViewController)
     func toContact(_: Contact, from: UIViewController)
     func toNickname(from: UIViewController, prefilled: String, _: @escaping StringClosure)
-    func toVerifying(from: UIViewController)
 }
 
 public struct RequestsCoordinator: RequestsCoordinating {
-    public init(searchFactory: @escaping () -> UIViewController) {
-        self.searchFactory = searchFactory
-    }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
+    var pushPresenter: Presenting = PushPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
 
-    // MARK: Factories
-
     var searchFactory: () -> UIViewController
-
-    var verifyingFactory: () -> UIViewController = VerifyingController.init
-
+    var verifyingFactory: () -> UIViewController
     var contactFactory: (Contact) -> UIViewController
-        = ContactController.init(_:)
-
     var nicknameFactory: (String, @escaping StringClosure) -> UIViewController
-        = NickameController.init(prefilled:_:)
+
+    public init(
+        searchFactory: @escaping () -> UIViewController,
+        verifyingFactory: @escaping () -> UIViewController,
+        contactFactory: @escaping (Contact) -> UIViewController,
+        nicknameFactory: @escaping (String, @escaping StringClosure) -> UIViewController
+    ) {
+        self.searchFactory = searchFactory
+        self.contactFactory = contactFactory
+        self.nicknameFactory = nicknameFactory
+        self.verifyingFactory = verifyingFactory
+    }
 }
 
 public extension RequestsCoordinator {
     func toSearch(from parent: UIViewController) {
         let screen = searchFactory()
-        pusher.present(screen, from: parent)
-    }
-
-    func toContact(
-        _ contact: Contact,
-        from parent: UIViewController
-    ) {
-        let screen = contactFactory(contact)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
     func toNickname(
@@ -61,4 +52,9 @@ public extension RequestsCoordinator {
         let screen = verifyingFactory()
         bottomPresenter.present(screen, from: parent)
     }
+
+    func toContact(_ contact: Contact, from parent: UIViewController) {
+        let screen = contactFactory(contact)
+        pushPresenter.present(screen, from: parent)
+    }
 }
diff --git a/Sources/RestoreFeature/Controllers/RestoreController.swift b/Sources/RestoreFeature/Controllers/RestoreController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1d87c70e5735f920d1b5b01f5c7aadfa17fb04eb
--- /dev/null
+++ b/Sources/RestoreFeature/Controllers/RestoreController.swift
@@ -0,0 +1,118 @@
+import UIKit
+import Models
+import Shared
+import Popup
+import Combine
+import DependencyInjection
+
+public final class RestoreController: UIViewController {
+    @Dependency private var coordinator: RestoreCoordinating
+
+    lazy private var screenView = RestoreView()
+
+    private let viewModel: RestoreViewModel
+    private var cancellables = Set<AnyCancellable>()
+    private var popupCancellables = Set<AnyCancellable>()
+
+    public init(_ ndf: String, _ settings: RestoreSettings) {
+        viewModel = .init(ndf: ndf, settings: settings)
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    public override func loadView() {
+        view = screenView
+        presentWarning()
+    }
+
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        setupNavigationBar()
+        setupBindings()
+    }
+
+    private func setupNavigationBar() {
+        navigationItem.backButtonTitle = ""
+
+        let title = UILabel()
+        title.text = Localized.Restore.header
+        title.textColor = Asset.neutralActive.color
+        title.font = Fonts.Mulish.semiBold.font(size: 18.0)
+
+        let back = UIButton.back()
+        back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside)
+
+        navigationItem.leftBarButtonItem = UIBarButtonItem(
+            customView: UIStackView(arrangedSubviews: [back, title])
+        )
+    }
+
+    private func setupBindings() {
+        viewModel.step
+            .receive(on: DispatchQueue.main)
+            .removeDuplicates()
+            .sink { [unowned self] in
+                screenView.updateFor(step: $0)
+
+                if $0 == .done {
+                    coordinator.toSuccess(from: self)
+                }
+            }.store(in: &cancellables)
+
+        screenView.backButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in didTapBack() }
+            .store(in: &cancellables)
+
+        screenView.cancelButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in didTapBack() }
+            .store(in: &cancellables)
+
+        screenView.restoreButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapRestore() }
+            .store(in: &cancellables)
+    }
+
+    @objc private func didTapBack() {
+        navigationController?.popViewController(animated: true)
+    }
+}
+
+extension RestoreController {
+    private func presentWarning() {
+        let actionButton = CapsuleButton()
+        actionButton.set(
+            style: .brandColored,
+            title: Localized.Restore.Warning.action
+        )
+
+        let popup = BottomPopup(with: [
+            PopupLabel(
+                font: Fonts.Mulish.bold.font(size: 26.0),
+                text: Localized.Restore.Warning.title,
+                color: Asset.neutralActive.color,
+                alignment: .left,
+                spacingAfter: 19
+            ),
+            PopupLabelAttributed(
+                text: Localized.Restore.Warning.subtitle,
+                spacingAfter: 37
+            ),
+            PopupStackView(views: [actionButton])
+        ])
+
+        actionButton.publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink {
+                popup.dismiss(animated: true) { [weak self] in
+                    guard let self = self else { return }
+                    self.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        coordinator.toPopup(popup, from: self)
+    }
+}
diff --git a/Sources/RestoreFeature/Controllers/RestoreListController.swift b/Sources/RestoreFeature/Controllers/RestoreListController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c4e31d9daf904c9f2ddd59fe225aec49beb63e58
--- /dev/null
+++ b/Sources/RestoreFeature/Controllers/RestoreListController.swift
@@ -0,0 +1,124 @@
+import HUD
+import Popup
+import Shared
+import UIKit
+import Combine
+import DependencyInjection
+
+public final class RestoreListController: UIViewController {
+    @Dependency private var hud: HUDType
+    @Dependency private var coordinator: RestoreCoordinating
+
+    lazy private var screenView = RestoreListView()
+
+    private let ndf: String
+    private let viewModel = RestoreListViewModel()
+    private var cancellables = Set<AnyCancellable>()
+    private var popupCancellables = Set<AnyCancellable>()
+
+    public override func loadView() {
+        view = screenView
+        presentWarning()
+    }
+
+    public init(_ ndf: String) {
+        self.ndf = ndf
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    public required init?(coder: NSCoder) { nil }
+
+    public override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        navigationController?.navigationBar.customize(translucent: true)
+    }
+
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        setupNavigationBar()
+        setupBindings()
+    }
+
+    private func setupNavigationBar() {
+        navigationItem.backButtonTitle = ""
+
+        let back = UIButton.back()
+        back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside)
+
+        navigationItem.leftBarButtonItem = UIBarButtonItem(
+            customView: UIStackView(arrangedSubviews: [back])
+        )
+    }
+
+    private func setupBindings() {
+        viewModel.hud
+            .receive(on: DispatchQueue.main)
+            .sink { [hud] in hud.update(with: $0) }
+            .store(in: &cancellables)
+
+        viewModel.didFetchBackup
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in coordinator.toRestore(using: ndf, with: $0, from: self) }
+            .store(in: &cancellables)
+
+        screenView.cancelButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in didTapBack() }
+            .store(in: &cancellables)
+
+        screenView.driveButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapCloud(.drive, from: self) }
+            .store(in: &cancellables)
+
+        screenView.icloudButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapCloud(.icloud, from: self) }
+            .store(in: &cancellables)
+
+        screenView.dropboxButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in viewModel.didTapCloud(.dropbox, from: self) }
+            .store(in: &cancellables)
+    }
+
+    @objc private func didTapBack() {
+        navigationController?.popViewController(animated: true)
+    }
+}
+
+extension RestoreListController {
+    private func presentWarning() {
+        let actionButton = CapsuleButton()
+        actionButton.set(
+            style: .brandColored,
+            title: Localized.Restore.Warning.action
+        )
+
+        let popup = BottomPopup(with: [
+            PopupLabel(
+                font: Fonts.Mulish.bold.font(size: 26.0),
+                text: Localized.Restore.Warning.title,
+                color: Asset.neutralActive.color,
+                alignment: .left,
+                spacingAfter: 19
+            ),
+            PopupLabelAttributed(
+                text: Localized.Restore.Warning.subtitle,
+                spacingAfter: 37
+            ),
+            PopupStackView(views: [actionButton])
+        ])
+
+        actionButton.publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink {
+                popup.dismiss(animated: true) { [weak self] in
+                    guard let self = self else { return }
+                    self.popupCancellables.removeAll()
+                }
+            }.store(in: &popupCancellables)
+
+        coordinator.toPopup(popup, from: self)
+    }
+}
diff --git a/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift b/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a0624385c1d0143c5826c358f7081ada3573f8b1
--- /dev/null
+++ b/Sources/RestoreFeature/Controllers/RestoreSuccessController.swift
@@ -0,0 +1,44 @@
+import UIKit
+import Combine
+import DependencyInjection
+
+public final class RestoreSuccessController: UIViewController {
+    @Dependency private var coordinator: RestoreCoordinating
+
+    lazy private var screenView = RestoreSuccessView()
+    private var cancellables = Set<AnyCancellable>()
+
+    public override func loadView() {
+        view = screenView
+    }
+
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        setupBindings()
+    }
+
+    public override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+
+        let gradient = CAGradientLayer()
+        gradient.colors = [
+            UIColor(red: 122/255, green: 235/255, blue: 239/255, alpha: 1).cgColor,
+            UIColor(red: 56/255, green: 204/255, blue: 232/255, alpha: 1).cgColor,
+            UIColor(red: 63/255, green: 186/255, blue: 253/255, alpha: 1).cgColor,
+            UIColor(red: 98/255, green: 163/255, blue: 255/255, alpha: 1).cgColor
+        ]
+
+        gradient.startPoint = CGPoint(x: 0, y: 0)
+        gradient.endPoint = CGPoint(x: 1, y: 1)
+
+        gradient.frame = screenView.bounds
+        screenView.layer.insertSublayer(gradient, at: 0)
+    }
+
+    private func setupBindings() {
+        screenView.nextButton
+            .publisher(for: .touchUpInside)
+            .sink { [unowned self] in coordinator.toChats(from: self) }
+            .store(in: &cancellables)
+    }
+}
diff --git a/Sources/RestoreFeature/Coordinator/RestoreCoordinator.swift b/Sources/RestoreFeature/Coordinator/RestoreCoordinator.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2ade73355fa9b2b3de55cac81fa3b11b9b37621b
--- /dev/null
+++ b/Sources/RestoreFeature/Coordinator/RestoreCoordinator.swift
@@ -0,0 +1,55 @@
+import UIKit
+import Models
+import Presentation
+
+public protocol RestoreCoordinating {
+    func toChats(from: UIViewController)
+    func toSuccess(from: UIViewController)
+    func toPopup(_: UIViewController, from: UIViewController)
+    func toRestore(using: String, with: RestoreSettings, from: UIViewController)
+}
+
+public struct RestoreCoordinator: RestoreCoordinating {
+    var pushPresenter: Presenting = PushPresenter()
+    var bottomPresenter: Presenting = BottomPresenter()
+    var replacePresenter: Presenting = ReplacePresenter()
+
+    var successFactory: () -> UIViewController
+    var chatListFactory: () -> UIViewController
+    var restoreFactory: (String, RestoreSettings) -> UIViewController
+
+    public init(
+        successFactory: @escaping () -> UIViewController,
+        chatListFactory: @escaping () -> UIViewController,
+        restoreFactory: @escaping (String, RestoreSettings) -> UIViewController
+    ) {
+        self.successFactory = successFactory
+        self.restoreFactory = restoreFactory
+        self.chatListFactory = chatListFactory
+    }
+}
+
+public extension RestoreCoordinator {
+    func toRestore(
+        using ndf: String,
+        with settings: RestoreSettings,
+        from parent: UIViewController
+    ) {
+        let screen = restoreFactory(ndf, settings)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toChats(from parent: UIViewController) {
+        let screen = chatListFactory()
+        replacePresenter.present(screen, from: parent)
+    }
+
+    func toSuccess(from parent: UIViewController) {
+        let screen = successFactory()
+        replacePresenter.present(screen, from: parent)
+    }
+
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
+    }
+}
diff --git a/Sources/RestoreFeature/Service/MockRestoreService.swift b/Sources/RestoreFeature/Service/MockRestoreService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1a013434211580df1c99ac1fa3842ab0fc797d59
--- /dev/null
+++ b/Sources/RestoreFeature/Service/MockRestoreService.swift
@@ -0,0 +1,30 @@
+import UIKit
+import Models
+import Combine
+import Foundation
+import GoogleDriveFeature
+import DependencyInjection
+
+public struct RestoreServiceMock: RestoreServiceType {
+    public var inProgress: AnyPublisher<Void, Never> {
+        fatalError()
+    }
+
+    public var settings: AnyPublisher<RestoreSettings, Never> {
+        fatalError()
+    }
+
+    public init() {}
+
+    public func didSelectBackup(at url: URL) {}
+
+    public func authorize(service: CloudService, from: UIViewController) {}
+
+    public func download(
+        from settings: RestoreSettings,
+        progress: @escaping RestoreProgress,
+        whenFinished: @escaping RestoreDownloadFinished
+    ) {
+        fatalError()
+    }
+}
diff --git a/Sources/RestoreFeature/Service/RestoreService.swift b/Sources/RestoreFeature/Service/RestoreService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..9bd5b80e169d75d89806e716116b4781a080f691
--- /dev/null
+++ b/Sources/RestoreFeature/Service/RestoreService.swift
@@ -0,0 +1,38 @@
+//import UIKit
+//import Models
+//import Combine
+//
+//import DependencyInjection
+//
+//public struct RestoreService: RestoreServiceType {
+//
+//
+//
+//    @Dependency private var coordinator: RestoreCoordinating
+//
+//    public var inProgress: AnyPublisher<Void, Never> { inProgressSubject.eraseToAnyPublisher() }
+//    public var settings: AnyPublisher<RestoreSettings, Never> { settingsSubject.eraseToAnyPublisher() }
+//
+//    private let inProgressSubject = PassthroughSubject<Void, Never>()
+//    private let settingsSubject = PassthroughSubject<RestoreSettings, Never>()
+//
+//    private var cancellables = Set<AnyCancellable>()
+//
+//    public init() {}
+//
+//    public func authorize(service: CloudService, from controller: UIViewController) {
+//        }
+//    }
+//
+//    public func download(
+//        from settings: RestoreSettings,
+//        progress: @escaping RestoreProgress,
+//        whenFinished: @escaping RestoreDownloadFinished
+//    ) {
+//        drive.downloadBackup(
+//            settings.backup!.id,
+//            progressCallback: progress,
+//            whenFinished
+//        )
+//    }
+//}
diff --git a/Sources/RestoreFeature/Service/RestoreServiceType.swift b/Sources/RestoreFeature/Service/RestoreServiceType.swift
new file mode 100644
index 0000000000000000000000000000000000000000..78a32e9f6d9e199d78fa9c17a4a06b7ca1de51f3
--- /dev/null
+++ b/Sources/RestoreFeature/Service/RestoreServiceType.swift
@@ -0,0 +1,20 @@
+import UIKit
+import Models
+import Combine
+
+public typealias RestoreProgress = (Float) -> Void
+public typealias RestoreDownloadFinished = (Result<Data, Error>) -> Void
+
+public protocol RestoreServiceType {
+    var inProgress: AnyPublisher<Void, Never> { get }
+
+    var settings: AnyPublisher<RestoreSettings, Never> { get }
+
+    func authorize(service: CloudService, from: UIViewController)
+
+    func download(
+        from settings: RestoreSettings,
+        progress: @escaping RestoreProgress,
+        whenFinished: @escaping RestoreDownloadFinished
+    )
+}
diff --git a/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..88d507c17b55eabdfd7ea24c8e22d8a3a609468d
--- /dev/null
+++ b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift
@@ -0,0 +1,120 @@
+import HUD
+import UIKit
+import Models
+import Shared
+import Combine
+import BackupFeature
+import DependencyInjection
+
+import iCloudFeature
+import DropboxFeature
+import GoogleDriveFeature
+
+final class RestoreListViewModel {
+    @Dependency private var icloud: iCloudInterface
+    @Dependency private var dropbox: DropboxInterface
+    @Dependency private var drive: GoogleDriveInterface
+
+    var hud: AnyPublisher<HUDStatus, Never> { hudSubject.eraseToAnyPublisher() }
+    var didFetchBackup: AnyPublisher<RestoreSettings, Never> { backupSubject.eraseToAnyPublisher() }
+
+    private var dropboxAuthCancellable: AnyCancellable?
+
+    private let hudSubject = PassthroughSubject<HUDStatus, Never>()
+    private let backupSubject = PassthroughSubject<RestoreSettings, Never>()
+
+    func didTapCloud(_ cloudService: CloudService, from parent: UIViewController) {
+        switch cloudService {
+        case .drive:
+            didRequestDriveAuthorization(from: parent)
+        case .icloud:
+            didRequestICloudAuthorization()
+        case .dropbox:
+            didRequestDropboxAuthorization(from: parent)
+        }
+    }
+
+    private func didRequestDriveAuthorization(from controller: UIViewController) {
+        drive.authorize(presenting: controller) { authResult in
+            switch authResult {
+            case .success:
+                self.hudSubject.send(.on)
+                self.drive.downloadMetadata { downloadResult in
+                    switch downloadResult {
+                    case .success(let metadata):
+                        var backup: Backup?
+
+                        if let metadata = metadata {
+                            backup = .init(id: metadata.identifier, date: metadata.modifiedDate, size: metadata.size)
+                        }
+
+                        self.hudSubject.send(.none)
+                        self.backupSubject.send(RestoreSettings(backup: backup, cloudService: .drive))
+
+                    case .failure(let error):
+                        self.hudSubject.send(.error(.init(with: error)))
+                    }
+                }
+            case .failure(let error):
+                self.hudSubject.send(.error(.init(with: error)))
+            }
+        }
+    }
+
+    private func didRequestICloudAuthorization() {
+        if icloud.isAuthorized() {
+            self.hudSubject.send(.on)
+
+            icloud.downloadMetadata { result in
+                switch result {
+                case .success(let metadata):
+                    var backup: Backup?
+
+                    if let metadata = metadata {
+                        backup = .init(id: metadata.path, date: metadata.modifiedDate, size: metadata.size)
+                    }
+
+                    self.hudSubject.send(.none)
+                    self.backupSubject.send(RestoreSettings(backup: backup, cloudService: .icloud))
+                case .failure(let error):
+                    self.hudSubject.send(.error(.init(with: error)))
+                }
+            }
+        } else {
+            /// This could be an alert controller asking if user wants to enable/deeplink
+            ///
+            icloud.openSettings()
+        }
+    }
+
+    private func didRequestDropboxAuthorization(from controller: UIViewController) {
+        dropboxAuthCancellable = dropbox.authorize(presenting: controller)
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] authResult in
+                switch authResult {
+                case .success(let bool):
+                    guard bool == true else { return }
+
+                    self.hudSubject.send(.on)
+                    dropbox.downloadMetadata { metadataResult in
+                        switch metadataResult {
+                        case .success(let metadata):
+                            var backup: Backup?
+
+                            if let metadata = metadata {
+                                backup = .init(id: metadata.path, date: metadata.modifiedDate, size: metadata.size)
+                            }
+
+                            self.hudSubject.send(.none)
+                            self.backupSubject.send(RestoreSettings(backup: backup, cloudService: .dropbox))
+
+                        case .failure(let error):
+                            self.hudSubject.send(.error(.init(with: error)))
+                        }
+                    }
+                case .failure(let error):
+                    self.hudSubject.send(.error(.init(with: error)))
+                }
+            }
+    }
+}
diff --git a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift
new file mode 100644
index 0000000000000000000000000000000000000000..24a3cfb6a91b001547f410882d2cfadd619623af
--- /dev/null
+++ b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift
@@ -0,0 +1,130 @@
+import UIKit
+import Models
+import Shared
+import Combine
+import Defaults
+import Foundation
+import Integration
+import BackupFeature
+import DependencyInjection
+
+import iCloudFeature
+import DropboxFeature
+import GoogleDriveFeature
+
+enum RestorationStep {
+    case idle(CloudService, Backup?)
+    case downloading(Float, Float)
+    case failDownload(Error)
+    case parsingData
+    case done
+}
+
+extension RestorationStep: Equatable {
+    static func ==(lhs: RestorationStep, rhs: RestorationStep) -> Bool {
+        switch (lhs, rhs) {
+        case (.done, .done):
+            return true
+        case let (.failDownload(a), .failDownload(b)):
+            return a.localizedDescription == b.localizedDescription
+        case let (.downloading(a, b), .downloading(c, d)):
+            return a == c && b == d
+        case (.idle, _), (.downloading, _), (.parsingData, _),
+            (.done, _), (.failDownload, _):
+            return false
+        }
+    }
+}
+
+final class RestoreViewModel {
+    @Dependency private var iCloudService: iCloudInterface
+    @Dependency private var dropboxService: DropboxInterface
+    @Dependency private var googleService: GoogleDriveInterface
+
+    @KeyObject(.username, defaultValue: nil) var username: String?
+
+    var step: AnyPublisher<RestorationStep, Never> { stepRelay.eraseToAnyPublisher() }
+
+    private let ndf: String
+    private let settings: RestoreSettings
+    private let stepRelay: CurrentValueSubject<RestorationStep, Never>
+
+    init(ndf: String, settings: RestoreSettings) {
+        self.ndf = ndf
+        self.settings = settings
+        self.stepRelay = .init(.idle(settings.cloudService, settings.backup))
+    }
+
+    func didTapRestore() {
+        guard let backup = settings.backup else { fatalError() }
+
+        stepRelay.send(.downloading(0.0, backup.size))
+
+        switch settings.cloudService {
+        case .drive:
+            downloadBackupForDrive(backup)
+        case .dropbox:
+            downloadBackupForDropbox(backup)
+        case .icloud:
+            downloadBackupForiCloud(backup)
+        }
+    }
+
+    private func downloadBackupForDropbox(_ backup: Backup) {
+        dropboxService.downloadBackup(backup.id) { [weak self] in
+            guard let self = self else { return }
+            self.stepRelay.send(.downloading(backup.size, backup.size))
+
+            switch $0 {
+            case .success(let data):
+                self.continueRestoring(data: data)
+            case .failure(let error):
+                self.stepRelay.send(.failDownload(error))
+            }
+        }
+    }
+
+    private func downloadBackupForiCloud(_ backup: Backup) {
+        iCloudService.downloadBackup(backup.id) { [weak self] in
+            guard let self = self else { return }
+            self.stepRelay.send(.downloading(backup.size, backup.size))
+
+            switch $0 {
+            case .success(let data):
+                self.continueRestoring(data: data)
+            case .failure(let error):
+                self.stepRelay.send(.failDownload(error))
+            }
+        }
+    }
+
+    private func downloadBackupForDrive(_ backup: Backup) {
+        googleService.downloadBackup(backup.id) { [weak self] in
+            if let stepRelay = self?.stepRelay {
+                stepRelay.send(.downloading($0, backup.size))
+            }
+        } _: { [weak self] in
+            guard let self = self else { return }
+
+            switch $0 {
+            case .success(let data):
+                self.continueRestoring(data: data)
+            case .failure(let error):
+                self.stepRelay.send(.failDownload(error))
+            }
+        }
+    }
+
+    private func continueRestoring(data: Data) {
+        stepRelay.send(.parsingData)
+
+        DispatchQueue.global().async { [weak self] in
+            guard let self = self else { return }
+
+            let session = try! Session(backupFile: data, ndf: self.ndf)
+            DependencyInjection.Container.shared.register(session as SessionType)
+
+            self.stepRelay.send(.done)
+        }
+    }
+}
diff --git a/Sources/RestoreFeature/Views/RestoreDetailsView.swift b/Sources/RestoreFeature/Views/RestoreDetailsView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..55f18c4d2befcd5dcfd9fa296fe7f6c888fd1aee
--- /dev/null
+++ b/Sources/RestoreFeature/Views/RestoreDetailsView.swift
@@ -0,0 +1,56 @@
+import UIKit
+import Shared
+
+final class RestoreDetailsView: UIView {
+    let separatorView = UIView()
+    let imageView = UIImageView()
+    let titleLabel = UILabel()
+
+    let stackView = UIStackView()
+    let dateView = DetailRowButton()
+    let sizeView = DetailRowButton()
+
+    init() {
+        super.init(frame: .zero)
+        separatorView.backgroundColor = Asset.neutralLine.color
+
+        titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0)
+        titleLabel.textColor = Asset.neutralActive.color
+
+        stackView.axis = .vertical
+        stackView.spacing = 22
+        stackView.addArrangedSubview(dateView)
+        stackView.addArrangedSubview(sizeView)
+
+        addSubview(separatorView)
+        addSubview(imageView)
+        addSubview(titleLabel)
+        addSubview(stackView)
+
+        separatorView.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview().offset(25)
+            make.right.equalToSuperview().offset(-25)
+            make.height.equalTo(1)
+        }
+
+        imageView.snp.makeConstraints { make in
+            make.top.equalTo(separatorView.snp.bottom).offset(40)
+            make.left.equalToSuperview().offset(24)
+        }
+
+        titleLabel.snp.makeConstraints { make in
+            make.centerY.equalTo(imageView)
+            make.left.equalToSuperview().offset(92)
+        }
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(20)
+            make.left.equalTo(titleLabel)
+            make.right.equalToSuperview().offset(-40)
+            make.bottom.lessThanOrEqualToSuperview().offset(-20)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/RestoreFeature/Views/RestoreListView.swift b/Sources/RestoreFeature/Views/RestoreListView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..26d937db458bc12f164141a32c665f1ac57ee12c
--- /dev/null
+++ b/Sources/RestoreFeature/Views/RestoreListView.swift
@@ -0,0 +1,123 @@
+import UIKit
+import Shared
+
+final class RestoreListView: UIView {
+    let titleLabel = UILabel()
+    let stackView = UIStackView()
+    let firstSubtitleLabel = UILabel()
+    let secondSubtitleLabel = UILabel()
+    let driveButton = RowButton()
+    let icloudButton = RowButton()
+    let dropboxButton = RowButton()
+    let cancelButton = CapsuleButton()
+
+    init() {
+        super.init(frame: .zero)
+        backgroundColor = Asset.neutralWhite.color
+
+        setupTitle(Localized.Restore.List.title)
+        setupSubtitle(Localized.Restore.List.firstSubtitle)
+
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineHeightMultiple = 1.15
+
+        let attrString = NSMutableAttributedString(
+            string: Localized.Restore.List.secondSubtitle,
+            attributes: [
+                .foregroundColor: Asset.neutralBody.color,
+                .font: Fonts.Mulish.regular.font(size: 16.0) as Any,
+                .paragraphStyle: paragraph
+            ]
+        )
+
+        secondSubtitleLabel.numberOfLines = 0
+        secondSubtitleLabel.attributedText = attrString
+
+        icloudButton.setup(title: Localized.Backup.iCloud, icon: Asset.restoreIcloud.image)
+        dropboxButton.setup(title: Localized.Backup.dropbox, icon: Asset.restoreDropbox.image)
+        driveButton.setup(title: Localized.Backup.googleDrive, icon: Asset.restoreDrive.image)
+
+        cancelButton.set(style: .seeThrough, title: Localized.Restore.List.cancel)
+
+        stackView.axis = .vertical
+        stackView.addArrangedSubview(driveButton)
+        stackView.addArrangedSubview(icloudButton)
+        stackView.addArrangedSubview(dropboxButton)
+
+        addSubview(titleLabel)
+        addSubview(firstSubtitleLabel)
+        addSubview(secondSubtitleLabel)
+        addSubview(stackView)
+        addSubview(cancelButton)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(safeAreaLayoutGuide).offset(15)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        firstSubtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(8)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        secondSubtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(firstSubtitleLabel.snp.bottom).offset(8)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-41)
+        }
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalTo(secondSubtitleLabel.snp.bottom).offset(28)
+            make.left.equalToSuperview().offset(24)
+            make.right.equalToSuperview().offset(-24)
+        }
+
+        cancelButton.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(20)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-40)
+            make.bottom.equalTo(safeAreaLayoutGuide).offset(-50)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    private func setupTitle(_ title: String) {
+        let attString = NSMutableAttributedString(string: title)
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineHeightMultiple = 1
+
+        attString.addAttribute(.paragraphStyle, value: paragraph)
+        attString.addAttribute(.foregroundColor, value: Asset.neutralActive.color)
+        attString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 34.0) as Any)
+
+        attString.addAttributes(attributes: [
+            .font: Fonts.Mulish.bold.font(size: 34.0) as Any,
+            .foregroundColor: Asset.brandPrimary.color
+        ], betweenCharacters: "#")
+
+        titleLabel.numberOfLines = 0
+        titleLabel.attributedText = attString
+    }
+
+    private func setupSubtitle(_ subtitle: String) {
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineHeightMultiple = 1.15
+
+        let attString = NSAttributedString(
+            string: subtitle,
+            attributes: [
+                .foregroundColor: Asset.neutralBody.color,
+                .font: Fonts.Mulish.regular.font(size: 16.0) as Any,
+                .paragraphStyle: paragraph
+            ])
+
+        firstSubtitleLabel.numberOfLines = 0
+        firstSubtitleLabel.attributedText = attString
+    }
+}
diff --git a/Sources/RestoreFeature/Views/RestoreProgressView.swift b/Sources/RestoreFeature/Views/RestoreProgressView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..95a471f02bf53bf936ca1cd30d98217bc3afde7c
--- /dev/null
+++ b/Sources/RestoreFeature/Views/RestoreProgressView.swift
@@ -0,0 +1,87 @@
+import UIKit
+import Shared
+
+final class RestoreProgressView: UIView {
+    let progressBarFull = UIView()
+    let progressBarFiller = UIView()
+    let progressLabel = UILabel()
+    let warningLabel = UILabel()
+    let descriptiveProgressLabel = UILabel()
+
+    init() {
+        super.init(frame: .zero)
+        warningLabel.textColor = Asset.neutralDisabled.color
+        progressLabel.textColor = Asset.neutralDisabled.color
+        descriptiveProgressLabel.textColor = Asset.neutralDisabled.color
+
+        warningLabel.font = Fonts.Mulish.regular.font(size: 14.0)
+        progressLabel.font = Fonts.Mulish.regular.font(size: 14.0)
+        descriptiveProgressLabel.font = Fonts.Mulish.regular.font(size: 14.0)
+
+        descriptiveProgressLabel.textAlignment = .center
+
+        progressBarFull.backgroundColor = Asset.neutralLine.color
+        progressBarFiller.backgroundColor = Asset.brandPrimary.color
+        progressBarFull.layer.masksToBounds = true
+        progressBarFull.layer.cornerRadius = 4
+
+        warningLabel.numberOfLines = 0
+        descriptiveProgressLabel.numberOfLines = 0
+        warningLabel.text = "This may take up to 5 mins, please don’t close the app and don’t put in background and don’t close your phone screen"
+
+        addSubview(progressBarFull)
+        addSubview(progressLabel)
+        addSubview(warningLabel)
+        addSubview(descriptiveProgressLabel)
+        progressBarFull.addSubview(progressBarFiller)
+
+        descriptiveProgressLabel.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualToSuperview()
+            make.left.equalToSuperview().offset(42)
+            make.right.equalToSuperview().offset(-42)
+            make.bottom.equalTo(progressBarFull.snp.top).offset(-15)
+        }
+
+        progressBarFull.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualToSuperview()
+            make.left.equalToSuperview().offset(42)
+            make.right.equalToSuperview().offset(-42)
+            make.centerY.equalToSuperview()
+            make.height.equalTo(8)
+        }
+
+        progressBarFiller.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+            make.width.equalTo(0)
+            make.bottom.equalToSuperview()
+        }
+
+        progressLabel.snp.makeConstraints { make in
+            make.top.equalTo(progressBarFull.snp.bottom).offset(15)
+            make.left.equalToSuperview().offset(42)
+            make.right.equalToSuperview().offset(-42)
+        }
+
+        warningLabel.snp.makeConstraints { make in
+            make.top.equalTo(progressLabel.snp.bottom).offset(15)
+            make.left.equalToSuperview().offset(42)
+            make.right.equalToSuperview().offset(-42)
+            make.bottom.lessThanOrEqualToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    func update(downloaded: Float, total: Float) {
+        let totalkb = String(format: "%.1f kb", total/1000)
+        let downloadedKb = String(format: "%.1f kb", downloaded/1000)
+        let percent = String(format: "%.0f", downloaded/total * 100)
+
+        progressLabel.text = "Downloaded \(downloadedKb) of \(totalkb) (\(percent)%)"
+
+        progressBarFiller.snp.updateConstraints { make in
+            make.width.equalTo(CGFloat(downloaded/total) * progressBarFull.frame.size.width)
+        }
+    }
+}
diff --git a/Sources/RestoreFeature/Views/RestoreSuccessView.swift b/Sources/RestoreFeature/Views/RestoreSuccessView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..94f318951e8d3f447d72a2d2188a37aca398b84f
--- /dev/null
+++ b/Sources/RestoreFeature/Views/RestoreSuccessView.swift
@@ -0,0 +1,78 @@
+import UIKit
+import Shared
+
+final class RestoreSuccessView: UIView {
+    let iconImageView = UIImageView()
+    let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
+    let nextButton = CapsuleButton()
+
+    init() {
+        super.init(frame: .zero)
+
+        iconImageView.contentMode = .center
+        iconImageView.image = Asset.onboardingSuccess.image
+        nextButton.set(style: .white, title: Localized.Onboarding.Success.action)
+
+        subtitleLabel.numberOfLines = 0
+        subtitleLabel.textColor = Asset.neutralWhite.color
+        subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+
+        addSubview(iconImageView)
+        addSubview(titleLabel)
+        addSubview(subtitleLabel)
+        addSubview(nextButton)
+
+        iconImageView.snp.makeConstraints { make in
+            make.top.equalTo(safeAreaLayoutGuide).offset(40)
+            make.left.equalToSuperview().offset(40)
+        }
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(iconImageView.snp.bottom).offset(40)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-90)
+        }
+
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(30)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-90)
+        }
+
+        nextButton.snp.makeConstraints { make in
+            make.left.equalToSuperview().offset(24)
+            make.right.equalToSuperview().offset(-24)
+            make.bottom.equalToSuperview().offset(-60)
+        }
+
+        setTitle(Localized.Restore.Success.title)
+        setSubtitle(Localized.Restore.Success.subtitle)
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    private func setTitle(_ title: String) {
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineHeightMultiple = 1.1
+
+        let attrString = NSMutableAttributedString(string: title)
+
+        attrString.addAttribute(.font, value: Fonts.Mulish.bold.font(size: 39.0))
+        attrString.addAttribute(.foregroundColor, value: Asset.neutralWhite.color)
+
+        attrString.addAttribute(
+            name: .foregroundColor,
+            value: Asset.neutralBody.color,
+            betweenCharacters: "#"
+        )
+
+        titleLabel.numberOfLines = 0
+        titleLabel.attributedText = attrString
+    }
+
+    private func setSubtitle(_ subtitle: String?) {
+        subtitleLabel.text = subtitle
+    }
+}
diff --git a/Sources/RestoreFeature/Views/RestoreView.swift b/Sources/RestoreFeature/Views/RestoreView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..cb7f0b7dcde49e40faa4872dcb99c88e6e3199f0
--- /dev/null
+++ b/Sources/RestoreFeature/Views/RestoreView.swift
@@ -0,0 +1,165 @@
+import UIKit
+import Shared
+import Models
+
+final class RestoreView: UIView {
+    let titleLabel = UILabel()
+    let subtitleLabel = UILabel()
+    let detailsView = RestoreDetailsView()
+    let progressView = RestoreProgressView()
+
+    let bottomStackView = UIStackView()
+    let backButton = CapsuleButton()
+    let cancelButton = CapsuleButton()
+    let restoreButton = CapsuleButton()
+
+    init() {
+        super.init(frame: .zero)
+        backgroundColor = Asset.neutralWhite.color
+
+        subtitleLabel.numberOfLines = 0
+        titleLabel.font = Fonts.Mulish.bold.font(size: 24.0)
+        subtitleLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+        titleLabel.textColor = Asset.neutralDark.color
+        subtitleLabel.textColor = Asset.neutralDark.color
+
+        restoreButton.set(style: .brandColored, title: Localized.Restore.Found.restore)
+        cancelButton.set(style: .simplestColoredBrand, title: Localized.Restore.Found.cancel)
+        backButton.set(style: .seeThrough, title: Localized.Restore.NotFound.back)
+
+        bottomStackView.axis = .vertical
+
+        addSubview(titleLabel)
+        addSubview(subtitleLabel)
+        addSubview(detailsView)
+        addSubview(progressView)
+        addSubview(bottomStackView)
+
+        bottomStackView.addArrangedSubview(restoreButton)
+        bottomStackView.addArrangedSubview(cancelButton)
+        bottomStackView.addArrangedSubview(backButton)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(safeAreaLayoutGuide).offset(20)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-38)
+        }
+
+        subtitleLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(20)
+            make.left.equalToSuperview().offset(38)
+            make.right.equalToSuperview().offset(-38)
+        }
+
+        detailsView.snp.makeConstraints { make in
+            make.top.equalTo(subtitleLabel.snp.bottom).offset(40)
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+        }
+
+        progressView.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualTo(detailsView.snp.bottom)
+            make.left.equalToSuperview()
+            make.right.equalToSuperview()
+            make.bottom.lessThanOrEqualTo(bottomStackView.snp.top)
+        }
+
+        bottomStackView.snp.makeConstraints { make in
+            make.top.greaterThanOrEqualTo(detailsView.snp.bottom).offset(10)
+            make.left.equalToSuperview().offset(40)
+            make.right.equalToSuperview().offset(-40)
+            make.bottom.equalTo(safeAreaLayoutGuide).offset(-20)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    func updateFor(step: RestorationStep) {
+        switch step {
+        case .idle(let cloudService, let backup):
+            guard let backup = backup else {
+                showNoBackupForCloud(named: cloudService.name())
+                return
+            }
+
+            showBackup(backup, fromCloud: cloudService)
+
+        case .downloading(let downloaded, let total):
+            restoreButton.isHidden = true
+            cancelButton.isHidden = true
+            progressView.isHidden = false
+
+            progressView.update(downloaded: downloaded, total: total)
+
+        case .failDownload(let error):
+            progressView.descriptiveProgressLabel.text = error.localizedDescription
+
+        case .parsingData:
+            progressView.descriptiveProgressLabel.text = "Parsing backup data"
+
+        case .done:
+            progressView.descriptiveProgressLabel.text = "Done"
+        }
+    }
+
+    private func showBackup(_ backup: Backup, fromCloud cloud: CloudService) {
+        titleLabel.text = Localized.Restore.Found.title
+        subtitleLabel.text = Localized.Restore.Found.subtitle
+
+        detailsView.titleLabel.text = cloud.name()
+        detailsView.imageView.image = cloud.asset()
+
+        detailsView.dateView.setup(
+            title: Localized.Restore.Found.date,
+            value: backup.date.backupStyle(),
+            hasArrow: false
+        )
+
+        detailsView.sizeView.setup(
+            title: Localized.Restore.Found.size,
+            value: String(format: "%.1f kb", backup.size/1000),
+            hasArrow: false
+        )
+
+        detailsView.isHidden = false
+        backButton.isHidden = true
+        restoreButton.isHidden = false
+        cancelButton.isHidden = false
+        progressView.isHidden = true
+    }
+
+    private func showNoBackupForCloud(named cloud: String) {
+        titleLabel.text = Localized.Restore.NotFound.title
+        subtitleLabel.text = Localized.Restore.NotFound.subtitle(cloud)
+
+        restoreButton.isHidden = true
+        cancelButton.isHidden = true
+        detailsView.isHidden = true
+        backButton.isHidden = false
+        progressView.isHidden = true
+    }
+}
+
+private extension CloudService {
+    func name() -> String {
+        switch self {
+        case .drive:
+            return Localized.Backup.googleDrive
+        case .icloud:
+            return Localized.Backup.iCloud
+        case .dropbox:
+            return Localized.Backup.dropbox
+        }
+    }
+
+    func asset() -> UIImage {
+        switch self {
+        case .drive:
+            return Asset.restoreDrive.image
+        case .icloud:
+            return Asset.restoreIcloud.image
+        case .dropbox:
+            return Asset.restoreDropbox.image
+        }
+    }
+}
diff --git a/Sources/ScanFeature/Coordinator/ScanCoordinator.swift b/Sources/ScanFeature/Coordinator/ScanCoordinator.swift
index 3993baffaf0fb1e47dde0c305a854a647c92d321..782d5b62980f165f27aa813be6294e6cdac221e3 100644
--- a/Sources/ScanFeature/Coordinator/ScanCoordinator.swift
+++ b/Sources/ScanFeature/Coordinator/ScanCoordinator.swift
@@ -11,52 +11,42 @@ public protocol ScanCoordinating {
 }
 
 public struct ScanCoordinator {
-    public init(
-        contactsFactory: @escaping () -> UIViewController,
-        requestsFactory: @escaping () -> UIViewController
-    ) {
-        self.contactsFactory = contactsFactory
-        self.requestsFactory = requestsFactory
-    }
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
+    var pushPresenter: Presenting = PushPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
-    var replacer: Presenting = ReplacePresenter(mode: .replaceLast)
-
-    // MARK: Factories
+    var replacePresenter: Presenting = ReplacePresenter(mode: .replaceLast)
 
     var contactsFactory: () -> UIViewController
     var requestsFactory: () -> UIViewController
-
     var contactFactory: (Contact) -> UIViewController
-        = ContactController.init(_:)
-}
 
-extension ScanCoordinator: ScanCoordinating {
-    public func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
+    public init(
+        contactsFactory: @escaping () -> UIViewController,
+        requestsFactory: @escaping () -> UIViewController,
+        contactFactory: @escaping (Contact) -> UIViewController
     ) {
-        bottomPresenter.present(popup, from: parent)
+        self.contactFactory = contactFactory
+        self.contactsFactory = contactsFactory
+        self.requestsFactory = requestsFactory
     }
+}
 
+extension ScanCoordinator: ScanCoordinating {
     public func toRequests(from parent: UIViewController) {
         let screen = requestsFactory()
-        replacer.present(screen, from: parent)
+        replacePresenter.present(screen, from: parent)
     }
 
     public func toContacts(from parent: UIViewController) {
         let screen = contactsFactory()
-        replacer.present(screen, from: parent)
+        replacePresenter.present(screen, from: parent)
     }
 
-    public func toContact(
-        _ contact: Contact,
-        from parent: UIViewController
-    ) {
+    public func toContact(_ contact: Contact, from parent: UIViewController) {
         let screen = contactFactory(contact)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
+    }
+
+    public func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
     }
 }
diff --git a/Sources/SearchFeature/Coordinator/SearchCoordinator.swift b/Sources/SearchFeature/Coordinator/SearchCoordinator.swift
index b62ec70f805c0a3104c558b97b3f533bcbd64bb5..0bb1448c964c26e399979a4a47a2f8ab98f5dec8 100644
--- a/Sources/SearchFeature/Coordinator/SearchCoordinator.swift
+++ b/Sources/SearchFeature/Coordinator/SearchCoordinator.swift
@@ -2,52 +2,41 @@ import UIKit
 import Models
 import Countries
 import Presentation
-import ContactFeature
 
 public protocol SearchCoordinating {
     func toContact(_: Contact, from: UIViewController)
     func toPopup(_: UIViewController, from: UIViewController)
-    func toCountries(from: UIViewController, _ onChoose: @escaping (Country) -> Void)
+    func toCountries(from: UIViewController, _: @escaping (Country) -> Void)
 }
 
 public struct SearchCoordinator {
-    public init() {}
-
-    // MARK: Presenters
-
-    var pusher: Presenting = PushPresenter()
+    var pushPresenter: Presenting = PushPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
 
-    // MARK: Factories
-
     var contactFactory: (Contact) -> UIViewController
-        = ContactController.init(_:)
-
     var countriesFactory: (@escaping (Country) -> Void) -> UIViewController
-        = CountryListController.init(_:)
+
+    public init(
+        contactFactory: @escaping (Contact) -> UIViewController,
+        countriesFactory: @escaping (@escaping (Country) -> Void) -> UIViewController
+    ) {
+        self.contactFactory = contactFactory
+        self.countriesFactory = countriesFactory
+    }
 }
 
 extension SearchCoordinator: SearchCoordinating {
-    public func toContact(
-        _ contact: Contact,
-        from parent: UIViewController
-    ) {
+    public func toContact(_ contact: Contact, from parent: UIViewController) {
         let screen = contactFactory(contact)
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    public func toCountries(
-        from parent: UIViewController,
-        _ onChoose: @escaping (Country) -> Void
-    ) {
-        let screen = countriesFactory(onChoose)
-        pusher.present(screen, from: parent)
+    public func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
     }
 
-    public func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(popup, from: parent)
+    public func toCountries(from parent: UIViewController, _ onChoose: @escaping (Country) -> Void) {
+        let screen = countriesFactory(onChoose)
+        pushPresenter.present(screen, from: parent)
     }
 }
diff --git a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
index eff3a3b4663af6871ffe74863389820162d80fb9..24f7ba5dea09f3f2cca6389607eaff6c840d8a96 100644
--- a/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
+++ b/Sources/SettingsFeature/Controllers/AccountDeleteController.swift
@@ -7,7 +7,7 @@ import Defaults
 import ScrollViewController
 import DependencyInjection
 
-final class AccountDeleteController: UIViewController {
+public final class AccountDeleteController: UIViewController {
     @KeyObject(.username, defaultValue: "") var username: String
 
     @Dependency private var hud: HUDType
@@ -20,13 +20,13 @@ final class AccountDeleteController: UIViewController {
     private var cancellables = Set<AnyCancellable>()
     private var popupCancellables = Set<AnyCancellable>()
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar
             .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupNavigationBar()
         setupScrollView()
diff --git a/Sources/SettingsFeature/Controllers/AdvancedController.swift b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift
similarity index 74%
rename from Sources/SettingsFeature/Controllers/AdvancedController.swift
rename to Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift
index e3df6d4ef0f8ead60698e6de4d2a5dbb37b43584..1645d25a32e6325eb902a122f2633cabdfd0cab7 100644
--- a/Sources/SettingsFeature/Controllers/AdvancedController.swift
+++ b/Sources/SettingsFeature/Controllers/SettingsAdvancedController.swift
@@ -3,25 +3,25 @@ import Shared
 import Combine
 import DependencyInjection
 
-final class AdvancedController: UIViewController {
+public final class SettingsAdvancedController: UIViewController {
     @Dependency private var coordinator: SettingsCoordinating
 
-    lazy private var screenView = AdvancedView()
+    lazy private var screenView = SettingsAdvancedView()
 
-    private let viewModel = AdvancedViewModel()
     private var cancellables = Set<AnyCancellable>()
+    private let viewModel = SettingsAdvancedViewModel()
 
-    override func loadView() {
+    public override func loadView() {
         view = screenView
     }
 
-    override func viewWillAppear(_ animated: Bool) {
+    public override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.navigationBar
             .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    override func viewDidLoad() {
+    public override func viewDidLoad() {
         super.viewDidLoad()
         setupNavigationBar()
         setupBindings()
@@ -46,31 +46,31 @@ final class AdvancedController: UIViewController {
     }
 
     private func setupBindings() {
-        viewModel.sharePublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in coordinator.toActivityController(with: [$0], from: self) }
-            .store(in: &cancellables)
-
-        screenView.downloadLogs
+        screenView.downloadLogsButton
             .publisher(for: .touchUpInside)
             .sink { [weak viewModel] in viewModel?.didTapDownloadLogs() }
             .store(in: &cancellables)
 
-        screenView.logs.switcherView
+        screenView.logRecordingSwitcher.switcherView
             .publisher(for: .valueChanged)
             .sink { [weak viewModel] in viewModel?.didToggleRecordLogs() }
             .store(in: &cancellables)
 
-        screenView.crashes.switcherView
+        screenView.crashReportingSwitcher.switcherView
             .publisher(for: .valueChanged)
             .sink { [weak viewModel] in viewModel?.didToggleCrashReporting() }
             .store(in: &cancellables)
 
+        viewModel.sharePublisher
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in coordinator.toActivityController(with: [$0], from: self) }
+            .store(in: &cancellables)
+
         viewModel.state
             .removeDuplicates()
             .sink { [unowned self] state in
-                screenView.logs.switcherView.setOn(state.isRecordingLogs, animated: true)
-                screenView.crashes.switcherView.setOn(state.isCrashReporting, animated: true)
+                screenView.logRecordingSwitcher.switcherView.setOn(state.isRecordingLogs, animated: true)
+                screenView.crashReportingSwitcher.switcherView.setOn(state.isCrashReporting, animated: true)
             }.store(in: &cancellables)
     }
 
diff --git a/Sources/SettingsFeature/Controllers/SettingsController.swift b/Sources/SettingsFeature/Controllers/SettingsController.swift
index 8426272f70a0b8ebc0e5709ff7f89ada52724958..fcf0aa2525e00479333eaaad5bb7e99c43c14ffb 100644
--- a/Sources/SettingsFeature/Controllers/SettingsController.swift
+++ b/Sources/SettingsFeature/Controllers/SettingsController.swift
@@ -52,11 +52,6 @@ public final class SettingsController: UIViewController {
             .customize(backgroundColor: Asset.neutralWhite.color)
     }
 
-    public override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        viewModel.didAppear()
-    }
-
     public override func viewDidLoad() {
         super.viewDidLoad()
 
@@ -100,15 +95,6 @@ public final class SettingsController: UIViewController {
             .sink { [hud] in hud.update(with: $0) }
             .store(in: &cancellables)
 
-        viewModel.infoPopupPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in
-                presentInfo(
-                    title: Localized.Settings.InfoPopUp.Privacy.title,
-                    subtitle: Localized.Settings.InfoPopUp.Privacy.subtitle
-                )
-            }.store(in: &cancellables)
-
         screenView.inAppNotifications.switcherView
             .publisher(for: .valueChanged)
             .sink { [weak viewModel] in viewModel?.didToggleInAppNotifications() }
@@ -139,7 +125,7 @@ public final class SettingsController: UIViewController {
             .sink { [weak viewModel] in viewModel?.didToggleBiometrics() }
             .store(in: &cancellables)
 
-        screenView.privacyPolicy
+        screenView.privacyPolicyButton
             .publisher(for: .touchUpInside)
             .receive(on: DispatchQueue.main)
             .sink { [unowned self] in
@@ -152,7 +138,7 @@ public final class SettingsController: UIViewController {
                     }
             }.store(in: &cancellables)
 
-        screenView.disclosures
+        screenView.disclosuresButton
             .publisher(for: .touchUpInside)
             .receive(on: DispatchQueue.main)
             .sink { [unowned self] in
@@ -165,14 +151,19 @@ public final class SettingsController: UIViewController {
                     }
             }.store(in: &cancellables)
 
-        screenView.delete
+        screenView.deleteButton
             .publisher(for: .touchUpInside)
             .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in
-                coordinator.toAccountDelete(from: self)
-            }.store(in: &cancellables)
+            .sink { [unowned self] in coordinator.toDelete(from: self) }
+            .store(in: &cancellables)
+
+        screenView.accountBackupButton
+            .publisher(for: .touchUpInside)
+            .receive(on: DispatchQueue.main)
+            .sink { [unowned self] in coordinator.toBackup(from: self) }
+            .store(in: &cancellables)
 
-        screenView.advanced
+        screenView.advancedButton
             .publisher(for: .touchUpInside)
             .receive(on: DispatchQueue.main)
             .sink { [unowned self] in coordinator.toAdvanced(from: self) }
diff --git a/Sources/SettingsFeature/Coordinator/SettingsCoordinator.swift b/Sources/SettingsFeature/Coordinator/SettingsCoordinator.swift
index de2df5e82f5a9994cfc84482c6e5ead6756a5291..6a3bf13774762095d79cacd7ac0e9e264871fc6f 100644
--- a/Sources/SettingsFeature/Coordinator/SettingsCoordinator.swift
+++ b/Sources/SettingsFeature/Coordinator/SettingsCoordinator.swift
@@ -3,54 +3,58 @@ import Shared
 import Presentation
 
 public protocol SettingsCoordinating {
+    func toBackup(from: UIViewController)
+    func toDelete(from: UIViewController)
     func toAdvanced(from: UIViewController)
-    func toAccountDelete(from: UIViewController)
     func toPopup(_: UIViewController, from: UIViewController)
     func toActivityController(with: [Any], from: UIViewController)
 }
 
 public struct SettingsCoordinator: SettingsCoordinating {
-    public init() {}
-
-    // MARK: Presenters
+    public init(
+        backupFactory: @escaping () -> UIViewController,
+        advancedFactory: @escaping () -> UIViewController,
+        accountDeleteFactory: @escaping () -> UIViewController
+    ) {
+        self.backupFactory = backupFactory
+        self.advancedFactory = advancedFactory
+        self.accountDeleteFactory = accountDeleteFactory
+    }
 
-    var pusher: Presenting = PushPresenter()
-    var presenter: Presenting = ModalPresenter()
+    var pushPresenter: Presenting = PushPresenter()
+    var modalPresenter: Presenting = ModalPresenter()
     var bottomPresenter: Presenting = BottomPresenter()
 
-    // MARK: Factories
-
-    var advancedFactory: () -> UIViewController = AdvancedController.init
-
-    var accountDeleteFactory: () -> UIViewController = AccountDeleteController.init
+    var backupFactory: () -> UIViewController
+    var advancedFactory: () -> UIViewController
+    var accountDeleteFactory: () -> UIViewController
 
     var activityControllerFactory: ([Any]) -> UIViewController
         = { UIActivityViewController(activityItems: $0, applicationActivities: nil) }
 }
 
 public extension SettingsCoordinator {
-    func toPopup(
-        _ popup: UIViewController,
-        from parent: UIViewController
-    ) {
-        bottomPresenter.present(popup, from: parent)
-    }
-
     func toAdvanced(from parent: UIViewController) {
         let screen = advancedFactory()
-        pusher.present(screen, from: parent)
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toActivityController(
-        with items: [Any],
-        from parent: UIViewController
-    ) {
-        let screen = activityControllerFactory(items)
-        presenter.present(screen, from: parent)
+    func toDelete(from parent: UIViewController) {
+        let screen = accountDeleteFactory()
+        pushPresenter.present(screen, from: parent)
     }
 
-    func toAccountDelete(from parent: UIViewController) {
-        let screen = accountDeleteFactory()
-        pusher.present(screen, from: parent)
+    func toBackup(from parent: UIViewController) {
+        let screen = backupFactory()
+        pushPresenter.present(screen, from: parent)
+    }
+
+    func toPopup(_ popup: UIViewController, from parent: UIViewController) {
+        bottomPresenter.present(popup, from: parent)
+    }
+
+    func toActivityController(with items: [Any], from parent: UIViewController) {
+        let screen = activityControllerFactory(items)
+        modalPresenter.present(screen, from: parent)
     }
 }
diff --git a/Sources/SettingsFeature/ViewModels/AdvancedViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift
similarity index 97%
rename from Sources/SettingsFeature/ViewModels/AdvancedViewModel.swift
rename to Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift
index 34b266a6c3020c33aea7c2a411ee76926686bf65..ed304656ddc9bf88a262bb01beb8c9aeb29194c9 100644
--- a/Sources/SettingsFeature/ViewModels/AdvancedViewModel.swift
+++ b/Sources/SettingsFeature/ViewModels/SettingsAdvancedViewModel.swift
@@ -10,7 +10,7 @@ struct AdvancedViewState: Equatable {
     var isCrashReporting = false
 }
 
-final class AdvancedViewModel {
+final class SettingsAdvancedViewModel {
     @KeyObject(.recordingLogs, defaultValue: true) var isRecordingLogs: Bool
     @KeyObject(.crashReporting, defaultValue: true) var isCrashReporting: Bool
 
diff --git a/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift b/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift
index 0e916ec9b54d3c221d6207bafa4dff4bcd460e87..a90685459ed3895ec092639512af84d11dff949d 100644
--- a/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift
+++ b/Sources/SettingsFeature/ViewModels/SettingsViewModel.swift
@@ -25,7 +25,6 @@ final class SettingsViewModel {
     @Dependency private var pushHandler: PushHandling
     @Dependency private var permissions: PermissionHandling
 
-    @KeyObject(.openedSettingsFirstTime, defaultValue: true) var isFirstTime: Bool
     @KeyObject(.dummyTrafficOn, defaultValue: false) var isDummyTrafficOn: Bool
     @KeyObject(.biometrics, defaultValue: false) private var biometrics
     @KeyObject(.hideAppList, defaultValue: false) private var hideAppList
@@ -38,12 +37,6 @@ final class SettingsViewModel {
     var hud: AnyPublisher<HUDStatus, Never> { hudRelay.eraseToAnyPublisher() }
     private let hudRelay = CurrentValueSubject<HUDStatus, Never>(.none)
 
-    var infoPopupPublisher: AnyPublisher<Void, Never> {
-        infoPopupSubject.eraseToAnyPublisher()
-    }
-
-    private let infoPopupSubject = PassthroughSubject<Void, Never>()
-
     var state: AnyPublisher<SettingsViewState, Never> { stateRelay.eraseToAnyPublisher() }
     private let stateRelay = CurrentValueSubject<SettingsViewState, Never>(.init())
 
@@ -57,12 +50,6 @@ final class SettingsViewModel {
         stateRelay.value.isDummyTrafficOn = isDummyTrafficOn
     }
 
-    func didAppear() {
-        guard isFirstTime else { return }
-        isFirstTime = false
-        infoPopupSubject.send()
-    }
-
     func didToggleBiometrics() {
         biometricAuthentication(enable: !biometrics)
     }
@@ -159,19 +146,3 @@ final class SettingsViewModel {
         }
     }
 }
-
-/*
-
- - case .appCancel:  The app canceled authentication by invalidating the LAContext
- - case .authenticationFailed: The user did not provide valid credentials
- - case .invalidContext: The LAContext was invalid
- - case .notInteractive: Interaction was not allowed so the authentication failed
- - case .passcodeNotSet: The user has not set a passcode on this device
- - case .systemCancel: The system canceled authentication for example to show another app
- - case .userCancel: The user canceled the authentication dialog
- - case .userFallback: The user selected to use a fallback authentication method
- - case .biometryLockout: Too many failed attempts locked biometric authentication
- - case .biometryNotAvailable: The user's device does not support biometric authentication
- - case .biometryNotEnrolled: The user has not configured biometric authentication
-
- */
diff --git a/Sources/SettingsFeature/Views/AccountDeleteView.swift b/Sources/SettingsFeature/Views/AccountDeleteView.swift
index 2dcc28e37f4a1a7db402e7cd46d5ca4206347379..e4aef7f5642929216a84e9e7b889ec0d3fa095ff 100644
--- a/Sources/SettingsFeature/Views/AccountDeleteView.swift
+++ b/Sources/SettingsFeature/Views/AccountDeleteView.swift
@@ -53,7 +53,7 @@ final class AccountDeleteView: UIView {
         confirmButton.setStyle(.red)
         confirmButton.isEnabled = false
         confirmButton.setTitle(Localized.Settings.Delete.delete, for: .normal)
-        cancelButton.setStyle(.simplestColored)
+        cancelButton.setStyle(.simplestColoredRed)
         cancelButton.setTitle(Localized.Settings.Delete.cancel, for: .normal)
 
         stackView.spacing = 12
diff --git a/Sources/SettingsFeature/Views/AdvancedView.swift b/Sources/SettingsFeature/Views/AdvancedView.swift
deleted file mode 100644
index 5baaac01cd6e25a961c3676361534b432505f301..0000000000000000000000000000000000000000
--- a/Sources/SettingsFeature/Views/AdvancedView.swift
+++ /dev/null
@@ -1,48 +0,0 @@
-import UIKit
-import Shared
-
-final class AdvancedView: UIView {
-    let stack = UIStackView()
-    let downloadLogs = UIButton()
-    let logs = SettingsSwitcher()
-    let crashes = SettingsSwitcher()
-
-    init() {
-        super.init(frame: .zero)
-        setup()
-    }
-
-    required init?(coder: NSCoder) { nil }
-
-    private func setup() {
-        backgroundColor = Asset.neutralWhite.color
-        downloadLogs.setImage(Asset.settingsDownload.image, for: .normal)
-
-        logs.set(
-            title: Localized.Settings.Advanced.Logs.title,
-            text: Localized.Settings.Advanced.Logs.description,
-            icon: Asset.settingsLogs.image,
-            extraAction: downloadLogs
-        )
-
-        crashes.set(
-            title: Localized.Settings.Advanced.Crashes.title,
-            text: Localized.Settings.Advanced.Crashes.description,
-            icon: Asset.settingsCrash.image,
-            separator: false
-        )
-
-        stack.spacing = 20
-        stack.axis = .vertical
-        stack.addArrangedSubview(logs)
-        stack.addArrangedSubview(crashes)
-
-        addSubview(stack)
-
-        stack.snp.makeConstraints { make in
-            make.top.equalToSuperview().offset(24)
-            make.left.equalToSuperview().offset(16)
-            make.right.equalToSuperview().offset(-16)
-        }
-    }
-}
diff --git a/Sources/SettingsFeature/Views/SettingsAdvancedView.swift b/Sources/SettingsFeature/Views/SettingsAdvancedView.swift
new file mode 100644
index 0000000000000000000000000000000000000000..e7f96ee81c740660545ef29d402c419046a8fbb6
--- /dev/null
+++ b/Sources/SettingsFeature/Views/SettingsAdvancedView.swift
@@ -0,0 +1,46 @@
+import UIKit
+import Shared
+
+final class SettingsAdvancedView: UIView {
+    let stackView = UIStackView()
+    let downloadLogsButton = UIButton()
+    let logRecordingSwitcher = SettingsSwitcher()
+    let crashReportingSwitcher = SettingsSwitcher()
+
+    init() {
+        super.init(frame: .zero)
+
+        backgroundColor = Asset.neutralWhite.color
+        downloadLogsButton.setImage(Asset.settingsDownload.image, for: .normal)
+
+        logRecordingSwitcher.set(
+            title: Localized.Settings.Advanced.Logs.title,
+            text: Localized.Settings.Advanced.Logs.description,
+            icon: Asset.settingsLogs.image,
+            extraAction: downloadLogsButton
+        )
+
+        crashReportingSwitcher.set(
+            title: Localized.Settings.Advanced.Crashes.title,
+            text: Localized.Settings.Advanced.Crashes.description,
+            icon: Asset.settingsCrash.image
+        )
+
+        stackView.axis = .vertical
+        stackView.addArrangedSubview(logRecordingSwitcher)
+        stackView.addArrangedSubview(crashReportingSwitcher)
+
+        stackView.setCustomSpacing(20, after: logRecordingSwitcher)
+        stackView.setCustomSpacing(10, after: crashReportingSwitcher)
+
+        addSubview(stackView)
+
+        stackView.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(24)
+            make.left.equalToSuperview().offset(16)
+            make.right.equalToSuperview().offset(-16)
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/SettingsFeature/Views/SettingsView.swift b/Sources/SettingsFeature/Views/SettingsView.swift
index 646dfbc565cf215f7f010f568ad5c9732d40f0de..8f17926c9bc885d82d5625d83eb7cfdf1eae6807 100644
--- a/Sources/SettingsFeature/Views/SettingsView.swift
+++ b/Sources/SettingsFeature/Views/SettingsView.swift
@@ -21,11 +21,12 @@ final class SettingsView: UIView {
     let hideActiveApp = SettingsSwitcher()
     let icognitoKeyboard = SettingsInfoSwitcher()
 
-    let otherStack = UIStackView()
-    let privacyPolicy = RowButton()
-    let disclosures = RowButton()
-    let advanced = RowButton()
-    let delete = RowButton()
+    let otherStackView = UIStackView()
+    let privacyPolicyButton = RowButton()
+    let disclosuresButton = RowButton()
+    let advancedButton = RowButton()
+    let accountBackupButton = RowButton()
+    let deleteButton = RowButton()
 
     let didTap: (InfoTapped) -> Void
 
@@ -132,38 +133,46 @@ final class SettingsView: UIView {
     }
 
     private func setupOtherStack() {
-        privacyPolicy.set(
+        privacyPolicyButton.setup(
             title: Localized.Settings.privacyPolicy,
             icon: Asset.settingsPrivacy.image,
             separator: false
         )
 
-        disclosures.set(
+        disclosuresButton.setup(
             title: Localized.Settings.disclosures,
             icon: Asset.settingsFolder.image
         )
 
-        advanced.set(
+        advancedButton.setup(
             title: Localized.Settings.advanced,
             icon: Asset.settingsAdvanced.image
         )
 
-        delete.set(
+        accountBackupButton.setup(
+            title: Localized.Settings.Advanced.AccountBackup.title,
+            icon: Asset.settingsAdvanced.image,
+            style: .clean,
+            separator: false
+        )
+
+        deleteButton.setup(
             title: Localized.Settings.delete,
             icon: Asset.settingsDelete.image,
             style: .delete,
             separator: false
         )
 
-        otherStack.axis = .vertical
-        otherStack.addArrangedSubview(privacyPolicy)
-        otherStack.addArrangedSubview(disclosures)
-        otherStack.addArrangedSubview(advanced)
-        otherStack.addArrangedSubview(delete)
+        otherStackView.axis = .vertical
+        otherStackView.addArrangedSubview(privacyPolicyButton)
+        otherStackView.addArrangedSubview(disclosuresButton)
+        otherStackView.addArrangedSubview(accountBackupButton)
+        otherStackView.addArrangedSubview(advancedButton)
+        otherStackView.addArrangedSubview(deleteButton)
 
-        addSubview(otherStack)
+        addSubview(otherStackView)
 
-        otherStack.snp.makeConstraints { make in
+        otherStackView.snp.makeConstraints { make in
             make.top.equalTo(chatStack.snp.bottom).offset(15)
             make.left.equalToSuperview().offset(16)
             make.right.equalToSuperview().offset(-16)
diff --git a/Sources/Shared/AutoGenerated/Assets.swift b/Sources/Shared/AutoGenerated/Assets.swift
index fd77d73250c3199e63ff4550827a05d278f01ef5..12a0ace416e24834f4c2089132bc2b773b74f75c 100644
--- a/Sources/Shared/AutoGenerated/Assets.swift
+++ b/Sources/Shared/AutoGenerated/Assets.swift
@@ -21,6 +21,7 @@ public typealias AssetImageTypeAlias = ImageAsset.Image
 
 // swiftlint:disable identifier_name line_length nesting type_body_length type_name
 public enum Asset {
+  public static let backupSuccess = ImageAsset(name: "backup_success")
   public static let chatAudioCloseSpeaker = ImageAsset(name: "chat_audio_close_speaker")
   public static let chatAudioOpenSpeaker = ImageAsset(name: "chat_audio_open_speaker")
   public static let chatAudioPause = ImageAsset(name: "chat_audio_pause")
@@ -87,6 +88,10 @@ public enum Asset {
   public static let requestsAccept = ImageAsset(name: "requests_accept")
   public static let requestsReceivedPlaceholder = ImageAsset(name: "requests_received_placeholder")
   public static let requestsReject = ImageAsset(name: "requests_reject")
+  public static let restoreDrive = ImageAsset(name: "restore_drive")
+  public static let restoreDropbox = ImageAsset(name: "restore_dropbox")
+  public static let restoreIcloud = ImageAsset(name: "restore_icloud")
+  public static let restoreSuccess = ImageAsset(name: "restore_success")
   public static let scanEmail = ImageAsset(name: "scan_email")
   public static let scanError = ImageAsset(name: "scan_error")
   public static let scanPhone = ImageAsset(name: "scan_phone")
diff --git a/Sources/Shared/AutoGenerated/Strings.swift b/Sources/Shared/AutoGenerated/Strings.swift
index 084fba8be2bca104ef28af65c1917237172eb1cf..4acbf48e1c5824279a950c20dde174079ce18550 100644
--- a/Sources/Shared/AutoGenerated/Strings.swift
+++ b/Sources/Shared/AutoGenerated/Strings.swift
@@ -172,6 +172,41 @@ public enum Localized {
     }
   }
 
+  public enum Backup {
+    /// Dropbox
+    public static let dropbox = Localized.tr("Localizable", "backup.dropbox")
+    /// Google Drive
+    public static let googleDrive = Localized.tr("Localizable", "backup.googleDrive")
+    /// Account Backup
+    public static let header = Localized.tr("Localizable", "backup.header")
+    /// iCloud
+    public static let iCloud = Localized.tr("Localizable", "backup.iCloud")
+    /// Back up your account to a cloud storage service, you can restore it along with your contacts when you reinstall xx messenger on another device.
+    public static let subtitle = Localized.tr("Localizable", "backup.subtitle")
+    public enum Config {
+      /// Backup now
+      public static let backupNow = Localized.tr("Localizable", "backup.config.backupNow")
+      /// Content backed up in %@ is not protected by xx network end-to-end encryption.
+      public static func disclaimer(_ p1: Any) -> String {
+        return Localized.tr("Localizable", "backup.config.disclaimer", String(describing: p1))
+      }
+      /// Backup to %@
+      public static func frequency(_ p1: Any) -> String {
+        return Localized.tr("Localizable", "backup.config.frequency", String(describing: p1))
+      }
+      /// Backup over
+      public static let infrastructure = Localized.tr("Localizable", "backup.config.infrastructure")
+      /// LATEST BACKUP
+      public static let latestBackup = Localized.tr("Localizable", "backup.config.latestBackup")
+      /// Backup settings
+      public static let title = Localized.tr("Localizable", "backup.config.title")
+    }
+    public enum Setup {
+      /// Setup your #backup service#.
+      public static let title = Localized.tr("Localizable", "backup.setup.title")
+    }
+  }
+
   public enum Chat {
     /// Cancel
     public static let cancel = Localized.tr("Localizable", "chat.cancel")
@@ -619,9 +654,13 @@ public enum Localized {
     public enum Success {
       /// Next
       public static let action = Localized.tr("Localizable", "onboarding.success.action")
-      /// Your #%@# has been successfully #added#.
-      public static func title(_ p1: Any) -> String {
-        return Localized.tr("Localizable", "onboarding.success.title", String(describing: p1))
+      public enum Email {
+        /// Your #email# has been successfully #added#.
+        public static let title = Localized.tr("Localizable", "onboarding.success.email.title")
+      }
+      public enum Phone {
+        /// Your #phone# has been successfully #added#.
+        public static let title = Localized.tr("Localizable", "onboarding.success.phone.title")
       }
     }
     public enum Username {
@@ -639,6 +678,12 @@ public enum Localized {
         /// Your Username
         public static let title = Localized.tr("Localizable", "onboarding.username.info.title")
       }
+      public enum Restore {
+        /// Restore From Backup
+        public static let action = Localized.tr("Localizable", "onboarding.username.restore.action")
+        /// Already have an account?
+        public static let title = Localized.tr("Localizable", "onboarding.username.restore.title")
+      }
     }
     public enum Welcome {
       /// Yes, continue
@@ -750,6 +795,61 @@ public enum Localized {
     }
   }
 
+  public enum Restore {
+    /// Account restore
+    public static let header = Localized.tr("Localizable", "restore.header")
+    public enum Found {
+      /// Cancel
+      public static let cancel = Localized.tr("Localizable", "restore.found.cancel")
+      /// BACKUP DATE
+      public static let date = Localized.tr("Localizable", "restore.found.date")
+      /// Next
+      public static let next = Localized.tr("Localizable", "restore.found.next")
+      /// Restore account
+      public static let restore = Localized.tr("Localizable", "restore.found.restore")
+      /// FILE SIZE
+      public static let size = Localized.tr("Localizable", "restore.found.size")
+      /// Restore your contacts from the following backup.
+      public static let subtitle = Localized.tr("Localizable", "restore.found.subtitle")
+      /// Backup found
+      public static let title = Localized.tr("Localizable", "restore.found.title")
+    }
+    public enum List {
+      /// Cancel
+      public static let cancel = Localized.tr("Localizable", "restore.list.cancel")
+      /// Restore your account from a previous backup. You’ll be able to have access to all your contacts.
+      public static let firstSubtitle = Localized.tr("Localizable", "restore.list.firstSubtitle")
+      /// Select the cloud storage service you previously used to create a backup.
+      public static let secondSubtitle = Localized.tr("Localizable", "restore.list.secondSubtitle")
+      /// Restore your #account#.
+      public static let title = Localized.tr("Localizable", "restore.list.title")
+    }
+    public enum NotFound {
+      /// Go back
+      public static let back = Localized.tr("Localizable", "restore.notFound.back")
+      /// No account backup was found in %@
+      public static func subtitle(_ p1: Any) -> String {
+        return Localized.tr("Localizable", "restore.notFound.subtitle", String(describing: p1))
+      }
+      /// Backup not found
+      public static let title = Localized.tr("Localizable", "restore.notFound.title")
+    }
+    public enum Success {
+      /// You now have access to all your contacts.
+      public static let subtitle = Localized.tr("Localizable", "restore.success.subtitle")
+      /// Your #account# has been successfully #restored#.
+      public static let title = Localized.tr("Localizable", "restore.success.title")
+    }
+    public enum Warning {
+      /// I understand
+      public static let action = Localized.tr("Localizable", "restore.warning.action")
+      /// xx messenger account can only run on a single device at a time. Using the same account on multiple devices may permanently damage your account and make it impossible to converse with your contacts
+      public static let subtitle = Localized.tr("Localizable", "restore.warning.subtitle")
+      /// Warning
+      public static let title = Localized.tr("Localizable", "restore.warning.title")
+    }
+  }
+
   public enum Scan {
     /// Go to contact
     public static let contact = Localized.tr("Localizable", "scan.contact")
@@ -825,6 +925,10 @@ public enum Localized {
     public enum Advanced {
       /// Advanced Settings
       public static let title = Localized.tr("Localizable", "settings.advanced.title")
+      public enum AccountBackup {
+        /// Account Backup
+        public static let title = Localized.tr("Localizable", "settings.advanced.accountBackup.title")
+      }
       public enum Crashes {
         /// Automatically sends anonymous reports containing crash data
         public static let description = Localized.tr("Localizable", "settings.advanced.crashes.description")
diff --git a/Sources/Shared/Extensions/Date.swift b/Sources/Shared/Extensions/Date.swift
index 5ffdaacf22dcc4b98246da2b1e243d8cf29bfa49..dd0fc91aaa80cfb18e028721d1ae617fd311354b 100644
--- a/Sources/Shared/Extensions/Date.swift
+++ b/Sources/Shared/Extensions/Date.swift
@@ -25,6 +25,17 @@ public extension Date {
         return formatter.string(for: self) ?? ""
     }
 
+    func backupStyle() -> String {
+        let formatter = DateFormatter()
+        formatter.dateFormat = DateFormatter.dateFormat(
+            fromTemplate: "MMM d, YYYY - h:mm",
+            options: 0,
+            locale: Locale(identifier: "en_US")
+        )
+
+        return formatter.string(from: self)
+    }
+
     static var asTimestamp: Int {
         Int(Date().timeIntervalSince1970).toNano()
     }
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..21dbcf85660641cd948448a96694c50b6f35b39c
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "filename" : "Group 512227.pdf",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Group 512227.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Group 512227.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..2cd77a744520d2830992a96604be01dfb3e925d4
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsBackup/backup_success.imageset/Group 512227.pdf	
@@ -0,0 +1,113 @@
+%PDF-1.7
+
+1 0 obj
+  << >>
+endobj
+
+2 0 obj
+  << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+32.000000 16.000000 m
+32.000000 7.163445 24.836555 0.000000 16.000000 0.000000 c
+7.163444 0.000000 0.000000 7.163445 0.000000 16.000000 c
+0.000000 24.836555 7.163444 32.000000 16.000000 32.000000 c
+24.836555 32.000000 32.000000 24.836555 32.000000 16.000000 c
+h
+W*
+n
+q
+1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
+0.929167 0.929167 0.929167 scn
+31.000000 16.000000 m
+31.000000 7.715729 24.284271 1.000000 16.000000 1.000000 c
+16.000000 -1.000000 l
+25.388842 -1.000000 33.000000 6.611158 33.000000 16.000000 c
+31.000000 16.000000 l
+h
+16.000000 1.000000 m
+7.715729 1.000000 1.000000 7.715729 1.000000 16.000000 c
+-1.000000 16.000000 l
+-1.000000 6.611158 6.611159 -1.000000 16.000000 -1.000000 c
+16.000000 1.000000 l
+h
+1.000000 16.000000 m
+1.000000 24.284271 7.715729 31.000000 16.000000 31.000000 c
+16.000000 33.000000 l
+6.611159 33.000000 -1.000000 25.388840 -1.000000 16.000000 c
+1.000000 16.000000 l
+h
+16.000000 31.000000 m
+24.284271 31.000000 31.000000 24.284271 31.000000 16.000000 c
+33.000000 16.000000 l
+33.000000 25.388840 25.388842 33.000000 16.000000 33.000000 c
+16.000000 31.000000 l
+h
+f
+n
+Q
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 8.634277 10.403870 cm
+0.172549 0.752941 0.411765 scn
+14.731392 10.017323 m
+4.714046 -0.000023 l
+0.000000 4.714022 l
+1.175000 5.889021 l
+4.714046 2.358311 l
+13.556392 11.192322 l
+14.731392 10.017323 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+  1393
+endobj
+
+4 0 obj
+  << /Annots []
+     /Type /Page
+     /MediaBox [ 0.000000 0.000000 32.000000 32.000000 ]
+     /Resources 1 0 R
+     /Contents 2 0 R
+     /Parent 5 0 R
+  >>
+endobj
+
+5 0 obj
+  << /Kids [ 4 0 R ]
+     /Count 1
+     /Type /Pages
+  >>
+endobj
+
+6 0 obj
+  << /Pages 5 0 R
+     /Type /Catalog
+  >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000001483 00000 n
+0000001506 00000 n
+0000001679 00000 n
+0000001753 00000 n
+trailer
+<< /ID [ (some) (id) ]
+   /Root 6 0 R
+   /Size 7
+>>
+startxref
+1812
+%%EOF
\ No newline at end of file
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..5c2f805eb3317d819659b5d73f14b6a1b249804f
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "filename" : "Icon.pdf",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Icon.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Icon.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a46bd03786ee3e3e3c937ae63fa938e5fb56e780
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_drive.imageset/Icon.pdf
@@ -0,0 +1,119 @@
+%PDF-1.7
+
+1 0 obj
+  << >>
+endobj
+
+2 0 obj
+  << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 19.556641 15.999268 cm
+0.678431 0.709804 0.741176 scn
+18.188225 0.000000 m
+18.264029 0.132510 l
+22.032003 6.602953 l
+22.260807 6.993652 l
+21.801113 6.993652 l
+4.226277 6.993652 l
+4.071191 6.993652 l
+3.995387 6.861826 l
+0.227413 0.390699 l
+0.000000 0.000000 l
+0.458304 0.000000 l
+18.033140 0.000000 l
+18.188225 0.000000 l
+h
+f
+n
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 24.521484 24.497009 cm
+0.678431 0.709804 0.741176 scn
+8.203583 15.502991 m
+8.048495 15.502308 l
+0.458305 15.472254 l
+0.000000 15.470204 l
+0.230196 15.080189 l
+9.017959 0.130486 l
+9.094460 0.000025 l
+9.249547 0.000025 l
+16.837650 0.032127 l
+17.296650 0.034177 l
+17.067152 0.422827 l
+8.279387 15.371847 l
+8.203583 15.502991 l
+h
+f
+n
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 14.000000 16.758179 cm
+0.678431 0.709804 0.741176 scn
+0.077891 6.829033 m
+3.900804 0.388641 l
+4.131000 -0.000008 l
+4.359804 0.390007 l
+13.147567 15.339723 l
+13.225458 15.469501 l
+13.147567 15.602011 l
+9.325349 22.041037 l
+9.094459 22.429688 l
+8.864959 22.039671 l
+0.077195 7.090637 l
+0.000000 6.960177 l
+0.077891 6.829033 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+  1138
+endobj
+
+4 0 obj
+  << /Annots []
+     /Type /Page
+     /MediaBox [ 0.000000 0.000000 56.000000 56.000000 ]
+     /Resources 1 0 R
+     /Contents 2 0 R
+     /Parent 5 0 R
+  >>
+endobj
+
+5 0 obj
+  << /Kids [ 4 0 R ]
+     /Count 1
+     /Type /Pages
+  >>
+endobj
+
+6 0 obj
+  << /Pages 5 0 R
+     /Type /Catalog
+  >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000001228 00000 n
+0000001251 00000 n
+0000001424 00000 n
+0000001498 00000 n
+trailer
+<< /ID [ (some) (id) ]
+   /Root 6 0 R
+   /Size 7
+>>
+startxref
+1557
+%%EOF
\ No newline at end of file
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..deb5a44b9e70a37a4dd49b15a309788bf25d4075
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "filename" : "Icon-3.pdf",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Icon-3.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Icon-3.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ad3060905eb2a0aba3f6190ff57813c85bb580df
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_dropbox.imageset/Icon-3.pdf
@@ -0,0 +1,95 @@
+%PDF-1.7
+
+1 0 obj
+  << >>
+endobj
+
+2 0 obj
+  << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+-1.000000 -0.000000 -0.000000 1.000000 43.000000 15.000000 cm
+0.678431 0.709804 0.741176 scn
+7.336956 25.000000 m
+0.000000 20.290787 l
+7.336956 15.651003 l
+14.674046 20.290787 l
+7.336956 25.000000 l
+h
+22.010607 25.000000 m
+14.674046 20.290787 l
+22.010870 15.651003 l
+29.347828 20.290787 l
+22.010607 25.000000 l
+h
+0.000000 10.941789 m
+7.336956 6.232576 l
+14.674046 10.941789 l
+7.336956 15.651003 l
+0.000000 10.941789 l
+h
+22.010607 15.651003 m
+14.674046 10.941789 l
+22.010870 6.232576 l
+29.347828 10.941789 l
+22.010607 15.651003 l
+h
+7.336956 4.708551 m
+14.674046 0.000000 l
+22.010870 4.709213 l
+14.674046 9.348997 l
+7.336956 4.708551 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+  672
+endobj
+
+4 0 obj
+  << /Annots []
+     /Type /Page
+     /MediaBox [ 0.000000 0.000000 56.000000 56.000000 ]
+     /Resources 1 0 R
+     /Contents 2 0 R
+     /Parent 5 0 R
+  >>
+endobj
+
+5 0 obj
+  << /Kids [ 4 0 R ]
+     /Count 1
+     /Type /Pages
+  >>
+endobj
+
+6 0 obj
+  << /Pages 5 0 R
+     /Type /Catalog
+  >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000000762 00000 n
+0000000784 00000 n
+0000000957 00000 n
+0000001031 00000 n
+trailer
+<< /ID [ (some) (id) ]
+   /Root 6 0 R
+   /Size 7
+>>
+startxref
+1090
+%%EOF
\ No newline at end of file
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..902114534e813c60b003270ace57ee2b9b436fc4
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "filename" : "Icon-2.pdf",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Icon-2.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Icon-2.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ccb74736c630534a457087fc953d992bf86d3f7c
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_icloud.imageset/Icon-2.pdf
@@ -0,0 +1,77 @@
+%PDF-1.7
+
+1 0 obj
+  << >>
+endobj
+
+2 0 obj
+  << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 12.000000 18.000000 cm
+0.678431 0.709804 0.741176 scn
+25.522648 0.000000 m
+6.264725 0.000000 l
+2.784380 0.000000 0.000000 2.758663 0.000000 6.206862 c
+0.000000 8.850319 1.624321 11.034307 4.060546 11.953918 c
+4.176483 14.252605 6.032679 16.091656 8.352796 16.091656 c
+9.177963 16.093571 9.985262 15.853600 10.673082 15.401948 c
+12.181294 18.275476 15.081611 20.000000 18.330082 20.000000 c
+22.970999 20.000000 26.798986 16.321558 26.914923 11.838881 c
+29.583193 11.149343 31.555841 8.735620 31.555841 5.976959 c
+31.555841 2.758831 28.887398 0.000170 25.522989 0.000170 c
+25.522648 0.000000 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+  667
+endobj
+
+4 0 obj
+  << /Annots []
+     /Type /Page
+     /MediaBox [ 0.000000 0.000000 56.000000 56.000000 ]
+     /Resources 1 0 R
+     /Contents 2 0 R
+     /Parent 5 0 R
+  >>
+endobj
+
+5 0 obj
+  << /Kids [ 4 0 R ]
+     /Count 1
+     /Type /Pages
+  >>
+endobj
+
+6 0 obj
+  << /Pages 5 0 R
+     /Type /Catalog
+  >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000000757 00000 n
+0000000779 00000 n
+0000000952 00000 n
+0000001026 00000 n
+trailer
+<< /ID [ (some) (id) ]
+   /Root 6 0 R
+   /Size 7
+>>
+startxref
+1085
+%%EOF
\ No newline at end of file
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..21dbcf85660641cd948448a96694c50b6f35b39c
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "filename" : "Group 512227.pdf",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true
+  }
+}
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Group 512227.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Group 512227.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..2cd77a744520d2830992a96604be01dfb3e925d4
--- /dev/null
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_success.imageset/Group 512227.pdf	
@@ -0,0 +1,113 @@
+%PDF-1.7
+
+1 0 obj
+  << >>
+endobj
+
+2 0 obj
+  << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+32.000000 16.000000 m
+32.000000 7.163445 24.836555 0.000000 16.000000 0.000000 c
+7.163444 0.000000 0.000000 7.163445 0.000000 16.000000 c
+0.000000 24.836555 7.163444 32.000000 16.000000 32.000000 c
+24.836555 32.000000 32.000000 24.836555 32.000000 16.000000 c
+h
+W*
+n
+q
+1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
+0.929167 0.929167 0.929167 scn
+31.000000 16.000000 m
+31.000000 7.715729 24.284271 1.000000 16.000000 1.000000 c
+16.000000 -1.000000 l
+25.388842 -1.000000 33.000000 6.611158 33.000000 16.000000 c
+31.000000 16.000000 l
+h
+16.000000 1.000000 m
+7.715729 1.000000 1.000000 7.715729 1.000000 16.000000 c
+-1.000000 16.000000 l
+-1.000000 6.611158 6.611159 -1.000000 16.000000 -1.000000 c
+16.000000 1.000000 l
+h
+1.000000 16.000000 m
+1.000000 24.284271 7.715729 31.000000 16.000000 31.000000 c
+16.000000 33.000000 l
+6.611159 33.000000 -1.000000 25.388840 -1.000000 16.000000 c
+1.000000 16.000000 l
+h
+16.000000 31.000000 m
+24.284271 31.000000 31.000000 24.284271 31.000000 16.000000 c
+33.000000 16.000000 l
+33.000000 25.388840 25.388842 33.000000 16.000000 33.000000 c
+16.000000 31.000000 l
+h
+f
+n
+Q
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 8.634277 10.403870 cm
+0.172549 0.752941 0.411765 scn
+14.731392 10.017323 m
+4.714046 -0.000023 l
+0.000000 4.714022 l
+1.175000 5.889021 l
+4.714046 2.358311 l
+13.556392 11.192322 l
+14.731392 10.017323 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+  1393
+endobj
+
+4 0 obj
+  << /Annots []
+     /Type /Page
+     /MediaBox [ 0.000000 0.000000 32.000000 32.000000 ]
+     /Resources 1 0 R
+     /Contents 2 0 R
+     /Parent 5 0 R
+  >>
+endobj
+
+5 0 obj
+  << /Kids [ 4 0 R ]
+     /Count 1
+     /Type /Pages
+  >>
+endobj
+
+6 0 obj
+  << /Pages 5 0 R
+     /Type /Catalog
+  >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000001483 00000 n
+0000001506 00000 n
+0000001679 00000 n
+0000001753 00000 n
+trailer
+<< /ID [ (some) (id) ]
+   /Root 6 0 R
+   /Size 7
+>>
+startxref
+1812
+%%EOF
\ No newline at end of file
diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsSettings/Icon-32.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsSettings/Icon-32.imageset/Contents.json
index 558dd2cf607944359cad5cf13c3e04cde1feb188..300616c824959c152991ea6b10c670a5cbe0ee0f 100644
--- a/Sources/Shared/Resources/Assets.xcassets/AssetsSettings/Icon-32.imageset/Contents.json
+++ b/Sources/Shared/Resources/Assets.xcassets/AssetsSettings/Icon-32.imageset/Contents.json
@@ -2,16 +2,7 @@
   "images" : [
     {
       "filename" : "Icon-32.pdf",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
+      "idiom" : "universal"
     }
   ],
   "info" : {
diff --git a/Sources/Shared/Resources/en.lproj/Localizable.strings b/Sources/Shared/Resources/en.lproj/Localizable.strings
index 1f51e0785c1c916600938ecf7d7ce85423892a67..152ed8db26af658560e757a240688efbd9d8cd4f 100644
--- a/Sources/Shared/Resources/en.lproj/Localizable.strings
+++ b/Sources/Shared/Resources/en.lproj/Localizable.strings
@@ -428,6 +428,34 @@
 = "Enable crash reporting";
 "settings.advanced.crashes.description"
 = "Automatically sends anonymous reports containing crash data";
+"settings.advanced.accountBackup.title"
+= "Account Backup";
+
+"backup.header"
+= "Account Backup";
+"backup.setup.title"
+= "Setup your #backup service#.";
+"backup.config.title"
+= "Backup settings";
+"backup.subtitle"
+= "Back up your account to a cloud storage service, you can restore it along with only your contacts when you reinstall xx Messenger on another device.";
+"backup.config.backupNow"
+= "Backup now";
+"backup.config.disclaimer"
+= "Content backed up in %@ is not protected by xx network end-to-end encryption.";
+"backup.config.latestBackup"
+= "LATEST BACKUP";
+"backup.config.frequency"
+= "Backup to %@";
+"backup.config.infrastructure"
+= "Backup over";
+
+"backup.iCloud"
+= "iCloud";
+"backup.dropbox"
+= "Dropbox";
+"backup.googleDrive"
+= "Google Drive";
 
 // Settings - Delete Account
 
@@ -517,6 +545,10 @@
 = "Your Username";
 "onboarding.username.info.subtitle"
 = "Your chosen username will be registered with the #User Discovery Service# allowing your public keys to be accessible to anyone who knows your username. They will then be able to send a request to create an authenticated channel with you. You will then be able to reject unwanted requests.";
+"onboarding.username.restore.title"
+= "Already have an account?";
+"onboarding.username.restore.action"
+= "Restore From Backup";
 
 // OnboardingFeature - Email
 
@@ -588,8 +620,10 @@
 
 // OnboardingFeature - Success
 
-"onboarding.success.title"
-= "Your #%@# has been successfully #added#.";
+"onboarding.success.email.title"
+= "Your #email# has been successfully #added#.";
+"onboarding.success.phone.title"
+= "Your #phone# has been successfully #added#.";
 "onboarding.success.action"
 = "Next";
 
@@ -615,6 +649,49 @@
 "onboarding.welcome.info.subtitle"
 = "Registration is completely optional. When registering an email or phone number, they will be evaluated by twilio, a 3rd party partner. Afterwards, salted hashes will be registered in #User Discovery# to allow other uses to search for you using the registered data completely privately.";
 
+"restore.warning.title"
+= "Warning";
+"restore.warning.subtitle"
+= "xx messenger account can only run on a single device at a time. Using the same account on multiple devices may permanently damage your account and make it impossible to converse with your contacts";
+"restore.warning.action"
+= "I understand";
+
+"restore.list.title"
+= "Restore your #account#.";
+"restore.list.firstSubtitle"
+= "Restore your account from a previous backup. You’ll be able to have access to all your contacts.";
+"restore.list.secondSubtitle"
+= "Select the cloud storage service you previously used to create a backup.";
+"restore.list.cancel"
+= "Cancel";
+
+"restore.header"
+= "Account restore";
+"restore.found.title"
+= "Backup found";
+"restore.found.subtitle"
+= "Restore your contacts from the following backup.";
+"restore.found.date"
+= "BACKUP DATE";
+"restore.found.size"
+= "FILE SIZE";
+"restore.found.restore"
+= "Restore account";
+"restore.found.cancel"
+= "Cancel";
+"restore.found.next"
+= "Next";
+"restore.notFound.title"
+= "Backup not found";
+"restore.notFound.subtitle"
+= "No account backup was found in %@";
+"restore.notFound.back"
+= "Go back";
+"restore.success.title"
+= "Your #account# has been successfully #restored#.";
+"restore.success.subtitle"
+= "You now have access to all your contacts.";
+
 // Shared
 
 "shared.search.placeholder"
diff --git a/Sources/Shared/Views/CapsuleButton.swift b/Sources/Shared/Views/CapsuleButton.swift
index 492bea9a6d424200c68c1514eccebb3cb23f6dff..96ef2e0963ec5ee20cc2b470f9a58c194847f936 100644
--- a/Sources/Shared/Views/CapsuleButton.swift
+++ b/Sources/Shared/Views/CapsuleButton.swift
@@ -65,13 +65,21 @@ public extension CapsuleButtonStyle {
         disabledTitleColor: Asset.brandPrimary.color.withAlphaComponent(0.5)
     )
 
-    static let simplestColored = CapsuleButtonStyle(
+    static let simplestColoredRed = CapsuleButtonStyle(
         fill: .color(UIColor.clear),
         borderWidth: 0,
         borderColor: nil,
         titleColor: Asset.accentDanger.color,
         disabledTitleColor: Asset.brandDefault.color.withAlphaComponent(0.5)
     )
+
+    static let simplestColoredBrand = CapsuleButtonStyle(
+        fill: .color(UIColor.clear),
+        borderWidth: 0,
+        borderColor: nil,
+        titleColor: Asset.brandPrimary.color,
+        disabledTitleColor: Asset.brandDefault.color.withAlphaComponent(0.5)
+    )
 }
 
 public final class CapsuleButton: UIButton {
diff --git a/Sources/Shared/Views/DetailRowButton.swift b/Sources/Shared/Views/DetailRowButton.swift
new file mode 100644
index 0000000000000000000000000000000000000000..132d499ac0e0223be1f9333cbd6e1643cfff7157
--- /dev/null
+++ b/Sources/Shared/Views/DetailRowButton.swift
@@ -0,0 +1,48 @@
+import UIKit
+
+public final class DetailRowButton: UIControl {
+    let titleLabel = UILabel()
+    let valueLabel = UILabel()
+    let rowIndicator = UIImageView()
+
+    public init() {
+        super.init(frame: .zero)
+
+        rowIndicator.contentMode = .center
+        rowIndicator.image = Asset.settingsDisclosure.image
+
+        titleLabel.font = Fonts.Mulish.bold.font(size: 12.0)
+        valueLabel.font = Fonts.Mulish.regular.font(size: 16.0)
+
+        titleLabel.textColor = Asset.neutralWeak.color
+        valueLabel.textColor = Asset.neutralActive.color
+
+        addSubview(titleLabel)
+        addSubview(valueLabel)
+        addSubview(rowIndicator)
+
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.left.equalToSuperview()
+        }
+
+        valueLabel.snp.makeConstraints { make in
+            make.top.equalTo(titleLabel.snp.bottom).offset(4)
+            make.left.equalToSuperview()
+            make.bottom.equalToSuperview()
+        }
+
+        rowIndicator.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.right.equalToSuperview()
+        }
+    }
+
+    required init?(coder: NSCoder) { nil }
+
+    public func setup(title: String, value: String, hasArrow: Bool = true) {
+        titleLabel.text = title
+        valueLabel.text = value
+        rowIndicator.isHidden = !hasArrow
+    }
+}
diff --git a/Sources/Shared/Views/RowButton.swift b/Sources/Shared/Views/RowButton.swift
index 0f6ab3887d1d240ba7a74ef2f81c437454c8a68a..689a705af98ce2364251f7d1d654de6eea3bd799 100644
--- a/Sources/Shared/Views/RowButton.swift
+++ b/Sources/Shared/Views/RowButton.swift
@@ -35,10 +35,7 @@ public final class RowButton: UIControl {
         stack.spacing = 10
         stack.addArrangedSubview(icon)
         stack.addArrangedSubview(title.pinning(at: .left(0)))
-        stack.addArrangedSubview(
-            accessory
-                .pinning(at: .top(10))
-        )
+        stack.addArrangedSubview(accessory.pinning(at: .top(10)))
 
         addSubview(stack)
         addSubview(separator)
@@ -62,7 +59,7 @@ public final class RowButton: UIControl {
 
     required init?(coder: NSCoder) { nil }
 
-    public func set(
+    public func setup(
         title: String,
         icon: UIImage,
         style: RowButtonStyle = .clean,
diff --git a/Sources/Shared/Views/RowSwitchableButton.swift b/Sources/Shared/Views/RowSwitchableButton.swift
new file mode 100644
index 0000000000000000000000000000000000000000..38136c3ef7f656e8df8907ef6b5a23d461b1adee
--- /dev/null
+++ b/Sources/Shared/Views/RowSwitchableButton.swift
@@ -0,0 +1,88 @@
+import UIKit
+
+public enum RowSwitchableButtonState {
+    case disclosure
+    case switcher(Bool)
+}
+
+public final class RowSwitchableButton: UIControl {
+    public let title = UILabel()
+    public let icon = UIImageView()
+    public let separator = UIView()
+
+    public let switcher = UISwitch()
+    public let disclosureIcon = UIImageView()
+
+    public init() {
+        super.init(frame: .zero)
+
+        icon.contentMode = .center
+        title.font = Fonts.Mulish.semiBold.font(size: 14.0)
+        separator.backgroundColor = Asset.neutralLine.color
+        title.textColor = Asset.neutralActive.color
+        disclosureIcon.image = Asset.settingsDisclosure.image
+        switcher.onTintColor = Asset.brandLight.color
+
+        addSubview(icon)
+        addSubview(title)
+        addSubview(disclosureIcon)
+        addSubview(switcher)
+        addSubview(separator)
+
+        icon.snp.makeConstraints { make in
+            make.top.equalToSuperview().offset(20)
+            make.left.equalToSuperview().offset(36)
+            make.bottom.equalToSuperview().offset(-20)
+        }
+
+        title.snp.makeConstraints { make in
+            make.left.equalTo(icon.snp.right).offset(25)
+            make.centerY.equalTo(icon)
+        }
+
+        disclosureIcon.snp.makeConstraints { make in
+            make.centerY.equalTo(icon)
+            make.right.equalToSuperview().offset(-48)
+        }
+
+        switcher.snp.makeConstraints { make in
+            make.right.equalToSuperview().offset(-25)
+            make.centerY.equalTo(icon)
+        }
+
+        separator.snp.makeConstraints { make in
+            make.height.equalTo(1)
+            make.left.equalToSuperview().offset(24)
+            make.right.equalToSuperview().offset(-24)
+            make.bottom.equalToSuperview()
+        }
+    }
+
+    public required init?(coder: NSCoder) { nil }
+
+    public func setup(
+        title: String,
+        icon: UIImage,
+        state: RowSwitchableButtonState = .disclosure,
+        separator: Bool = true
+    ) {
+        self.icon.image = icon
+        self.title.text = title
+
+        switch state {
+        case .disclosure:
+            switcher.isHidden = true
+            disclosureIcon.isHidden = false
+
+        case .switcher(let bool):
+            switcher.isOn = bool
+            switcher.isHidden = false
+            disclosureIcon.isHidden = true
+        }
+
+        guard separator == true else {
+            self.separator.removeFromSuperview()
+            return
+        }
+    }
+}
diff --git a/Sources/iCloudFeature/iCloudInterface.swift b/Sources/iCloudFeature/iCloudInterface.swift
new file mode 100644
index 0000000000000000000000000000000000000000..41b3153667013967f53fab9eb01d95a99293297c
--- /dev/null
+++ b/Sources/iCloudFeature/iCloudInterface.swift
@@ -0,0 +1,13 @@
+import Foundation
+
+public protocol iCloudInterface {
+    func openSettings()
+
+    func isAuthorized() -> Bool
+
+    func downloadMetadata(_: @escaping (Result<iCloudMetadata?, Error>) -> Void)
+
+    func uploadBackup(_: URL, _: @escaping (Result<iCloudMetadata, Error>) -> Void)
+
+    func downloadBackup(_: String, _: @escaping (Result<Data, Error>) -> Void)
+}
diff --git a/Sources/iCloudFeature/iCloudMetadata.swift b/Sources/iCloudFeature/iCloudMetadata.swift
new file mode 100644
index 0000000000000000000000000000000000000000..9aa70514badbef94033ec24bc965f49b23567e21
--- /dev/null
+++ b/Sources/iCloudFeature/iCloudMetadata.swift
@@ -0,0 +1,17 @@
+import Foundation
+
+public struct iCloudMetadata: Equatable {
+    public var size: Float
+    public var path: String
+    public var modifiedDate: Date
+
+    public init(
+        path: String,
+        size: Float,
+        modifiedDate: Date
+    ) {
+        self.path = path
+        self.size = size
+        self.modifiedDate = modifiedDate
+    }
+}
diff --git a/Sources/iCloudFeature/iCloudService.swift b/Sources/iCloudFeature/iCloudService.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2ccf20ba26357c9e3966b88fe17f9941fd7525d4
--- /dev/null
+++ b/Sources/iCloudFeature/iCloudService.swift
@@ -0,0 +1,87 @@
+import UIKit
+import FilesProvider
+
+public struct iCloudService: iCloudInterface {
+    private let documentsProvider = CloudFileProvider(containerId: "iCloud.xxm-cloud", scope: .data)
+
+    public init() {}
+
+    public func isAuthorized() -> Bool {
+        FileManager.default.ubiquityIdentityToken != nil
+    }
+
+    public func openSettings() {
+        if let url = URL(string: "App-Prefs:root=CASTLE"), UIApplication.shared.canOpenURL(url) {
+            UIApplication.shared.open(url, options: [:], completionHandler: nil)
+        }
+    }
+
+    public func downloadMetadata(_ completion: @escaping (Result<iCloudMetadata?, Error>) -> Void) {
+        guard let documentsProvider = documentsProvider else { fatalError() }
+
+        documentsProvider.contentsOfDirectory(path: "/", completionHandler: { contents, error in
+            guard error == nil else {
+                print(">>> [iCloud] downloadMetadata got error: \(error!.localizedDescription)")
+                completion(.failure(error!))
+                return
+            }
+
+            print(contents)
+
+            if let file = contents.first(where: { $0.name == "backup.xxm" }) {
+                completion(.success(.init(
+                    path: file.path,
+                    size: Float(file.size),
+                    modifiedDate: file.modifiedDate!
+                )))
+            } else {
+                completion(.success(nil))
+            }
+        })
+    }
+
+    public func uploadBackup(_ url: URL, _ completion: @escaping (Result<iCloudMetadata, Error>) -> Void) {
+        guard let documentsProvider = documentsProvider else { fatalError() }
+
+        do {
+            let data = try Data(contentsOf: url)
+
+            documentsProvider.writeContents(path: "backup.xxm", contents: data, overwrite: true) { error in
+                guard error == nil else {
+                    print(">>> [iCloud] uploadBackup got error: \(error!.localizedDescription)")
+                    completion(.failure(error!))
+                    return
+                }
+
+                completion(.success(.init(
+                    path: "backup.xxm",
+                    size: Float(data.count),
+                    modifiedDate: Date()
+                )))
+            }
+        } catch {
+            completion(.failure(error))
+        }
+    }
+
+    public func downloadBackup(
+        _ path: String,
+        _ completion: @escaping (Result<Data, Error>) -> Void
+    ) {
+        guard let documentsProvider = documentsProvider else { fatalError() }
+
+        documentsProvider.contents(path: path, completionHandler: { contents, error in
+            guard error == nil else {
+                print(">>> [iCloud] downloadBackup got error: \(error!.localizedDescription)")
+                completion(.failure(error!))
+                return
+            }
+
+            if let contents = contents {
+                completion(.success(contents))
+            } else {
+                completion(.failure(NSError(domain: "Backup file is invalid", code: 0)))
+            }
+        })
+    }
+}
diff --git a/Sources/iCloudFeature/iCloudServiceMock.swift b/Sources/iCloudFeature/iCloudServiceMock.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f0bcfca66108e297f5d04de4fb3e5dc0705ae097
--- /dev/null
+++ b/Sources/iCloudFeature/iCloudServiceMock.swift
@@ -0,0 +1,39 @@
+import Foundation
+
+public struct iCloudServiceMock: iCloudInterface {
+    public init() {
+        // TODO
+    }
+
+    public func openSettings() {
+        // TODO
+    }
+
+    public func isAuthorized() -> Bool {
+        true
+    }
+
+    public func downloadBackup(
+        _: String,
+        _: @escaping (Result<Data, Error>) -> Void
+    ) {
+        // TODO
+    }
+
+    public func uploadBackup(
+        _: URL,
+        _: @escaping (Result<iCloudMetadata, Error>) -> Void
+    ) {
+        // TODO
+    }
+
+    public func downloadMetadata(
+        _ completion: @escaping (Result<iCloudMetadata?, Error>) -> Void
+    ) {
+        completion(.success(.init(
+            path: "/",
+            size: 1230000000.0,
+            modifiedDate: Date()
+        )))
+    }
+}
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings
index e9720c545e7eceba5d6946f435854e0019614dc2..132638347cca07890832d2d4cc9508a49422aa95 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings
index e9720c545e7eceba5d6946f435854e0019614dc2..132638347cca07890832d2d4cc9508a49422aa95 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/A/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings
index e9720c545e7eceba5d6946f435854e0019614dc2..132638347cca07890832d2d4cc9508a49422aa95 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings
index 62d3a3ad56dc1c079e800f5aa915479f08533760..70d29b99750c2894dd0cde34438a187dd60ea325 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings
index 62d3a3ad56dc1c079e800f5aa915479f08533760..70d29b99750c2894dd0cde34438a187dd60ea325 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/A/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings
index 62d3a3ad56dc1c079e800f5aa915479f08533760..70d29b99750c2894dd0cde34438a187dd60ea325 100644
Binary files a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings and b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Bindings differ
diff --git a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
index 5a5d5963442bdd4d0b36d2f4540903d62aacde94..209b34eb81ea47e4d05a9163fa87b2151842d99e 100644
--- a/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
+++ b/XCFrameworks/Bindings.xcframework/ios-arm64_x86_64-simulator/Bindings.framework/Versions/Current/Headers/Bindings.objc.h
@@ -12,6 +12,7 @@
 
 
 @class BindingsBackup;
+@class BindingsBackupReport;
 @class BindingsClient;
 @class BindingsContact;
 @class BindingsContactList;
@@ -45,8 +46,8 @@
 @class BindingsAuthConfirmCallback;
 @protocol BindingsAuthRequestCallback;
 @class BindingsAuthRequestCallback;
-@protocol BindingsAuthResetCallback;
-@class BindingsAuthResetCallback;
+@protocol BindingsAuthResetNotificationCallback;
+@class BindingsAuthResetNotificationCallback;
 @protocol BindingsClientError;
 @class BindingsClientError;
 @protocol BindingsEventCallbackFunctionObject;
@@ -98,7 +99,7 @@
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
-@protocol BindingsAuthResetCallback <NSObject>
+@protocol BindingsAuthResetNotificationCallback <NSObject>
 - (void)callback:(BindingsContact* _Nullable)requestor;
 @end
 
@@ -193,6 +194,10 @@
 
 - (nonnull instancetype)initWithRef:(_Nonnull id)ref;
 - (nonnull instancetype)init;
+/**
+ * AddJson stores a passed in json string in the backup structure
+ */
+- (void)addJson:(NSString* _Nullable)json;
 /**
  * IsBackupRunning returns true if the backup has been initialized and is
 running. Returns false if it has been stopped.
@@ -205,6 +210,17 @@ storage. To enable backups again, call InitializeBackup.
 - (BOOL)stopBackup:(NSError* _Nullable* _Nullable)error;
 @end
 
+@interface BindingsBackupReport : NSObject <goSeqRefInterface> {
+}
+@property(strong, readonly) _Nonnull id _ref;
+
+- (nonnull instancetype)initWithRef:(_Nonnull id)ref;
+- (nonnull instancetype)init;
+// skipped field BackupReport.RestoredContacts with unsupported type: []*gitlab.com/xx_network/primitives/id.ID
+
+@property (nonatomic) NSString* _Nonnull params;
+@end
+
 /**
  * BindingsClient wraps the api.Client, implementing additional functions
 to support the gomobile Client interface
@@ -282,7 +298,7 @@ Running	- 2000
 Stopping	- 3000
  */
 - (long)networkFollowerStatus;
-- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetCallback> _Nullable)reset;
+- (void)registerAuthCallbacks:(id<BindingsAuthRequestCallback> _Nullable)request confirm:(id<BindingsAuthConfirmCallback> _Nullable)confirm reset:(id<BindingsAuthResetNotificationCallback> _Nullable)reset;
 /**
  * RegisterClientErrorCallback registers the callback to handle errors from the
 long running threads controlled by StartNetworkFollower and StopNetworkFollower
@@ -675,11 +691,6 @@ the period.
 The period is specified in milliseconds.
  */
 - (BOOL)registerSendProgressCallback:(NSData* _Nullable)transferID progressFunc:(id<BindingsFileTransferSentProgressFunc> _Nullable)progressFunc periodMS:(long)periodMS error:(NSError* _Nullable* _Nullable)error;
-/**
- * Resend resends a file if sending fails. This function should only be called
-if the interfaces.SentProgressCallback returns an error.
- */
-- (BOOL)resend:(NSData* _Nullable)transferID error:(NSError* _Nullable* _Nullable)error;
 /**
  * Send sends a file to the recipient. The sender must have an E2E relationship
 with the recipient.
@@ -1140,6 +1151,10 @@ for determining which IDs restored, which failed, and why.
  * GetFailedAt returns the failed ID at index
  */
 - (NSData* _Nullable)getFailedAt:(long)index;
+/**
+ * GetRestoreContactsError returns an error string. Empty if no error.
+ */
+- (NSString* _Nonnull)getRestoreContactsError;
 /**
  * GetRestoredAt returns the restored ID at index
  */
@@ -1259,6 +1274,28 @@ for the life of the program.
 This must be called while start network follower is running.
  */
 - (nullable instancetype)init:(BindingsClient* _Nullable)client;
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+- (nullable instancetype)initFromBackup:(BindingsClient* _Nullable)client email:(NSString* _Nullable)email phone:(NSString* _Nullable)phone;
 /**
  * AddFact adds a fact for the user to user discovery. Will only succeed if the
 user is already registered and the system does not have the fact currently
@@ -1270,20 +1307,6 @@ associated with, a code will be sent. This confirmation ID needs to be
 called along with the code to finalize the fact.
  */
 - (NSString* _Nonnull)addFact:(NSString* _Nullable)fStr error:(NSError* _Nullable* _Nullable)error;
-/**
- * BackUpMissingFacts adds a registered fact to the Store object and saves
-it to storage. It can take in both an email or a phone number, passed into
-the function in that order.  Any one of these fields may be empty,
-however both fields being empty will cause an error. Any other fact that is not
-an email or phone number will return an error. You may only add a fact for the
-accepted types once each. If you attempt to back up a fact type that has already
-been backed up, an error will be returned. Anytime an error is returned, it means
-the backup was not successful.
-NOTE: Do not use this as a direct store operation. This feature is intended to add facts
-to a backend store that have ALREADY BEEN REGISTERED on the account.
-THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
- */
-- (BOOL)backUpMissingFacts:(NSString* _Nullable)email phone:(NSString* _Nullable)phone error:(NSError* _Nullable* _Nullable)error;
 /**
  * ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 AddFact while the code will come over the associated communications system
@@ -1523,7 +1546,7 @@ FOUNDATION_EXPORT BOOL BindingsNewClient(NSString* _Nullable network, NSString*
  * NewClientFromBackup constructs a new Client from an encrypted backup. The backup
 is decrypted using the backupPassphrase. On success a successful client creation,
 the function will return a JSON encoded list of the E2E partners
-contained in the backup.
+contained in the backup and a json-encoded string of the parameters stored in the backup
  */
 FOUNDATION_EXPORT NSData* _Nullable BindingsNewClientFromBackup(NSString* _Nullable ndfJSON, NSString* _Nullable storageDir, NSData* _Nullable sessionPassword, NSData* _Nullable backupPassphrase, NSData* _Nullable backupFileContents, NSError* _Nullable* _Nullable error);
 
@@ -1597,6 +1620,29 @@ This must be called while start network follower is running.
  */
 FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscovery(BindingsClient* _Nullable client, NSError* _Nullable* _Nullable error);
 
+/**
+ * NewUserDiscoveryFromBackup returns a new user discovery object. It
+wil set up the manager with the backup data. Pass into it the backed up
+facts, one email and phone number each. This will add the registered facts
+to the backed Store. Any one of these fields may be empty,
+however both fields being empty will cause an error. Any other fact that is not
+an email or phone number will return an error. You may only add a fact for the
+accepted types once each. If you attempt to back up a fact type that has already
+been backed up, an error will be returned. Anytime an error is returned, it means
+the backup was not successful.
+NOTE: Do not use this as a direct store operation. This feature is intended to add facts
+to a backend store that have ALREADY BEEN REGISTERED on the account.
+THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
+Only call this once. It must be called after StartNetworkFollower
+is called and will fail if the network has never been contacted.
+This function technically has a memory leak because it causes both sides of
+the bindings to think the other is in charge of the client object.
+In general this is not an issue because the client object should exist
+for the life of the program.
+This must be called while start network follower is running.
+ */
+FOUNDATION_EXPORT BindingsUserDiscovery* _Nullable BindingsNewUserDiscoveryFromBackup(BindingsClient* _Nullable client, NSString* _Nullable email, NSString* _Nullable phone, NSError* _Nullable* _Nullable error);
+
 /**
  * NotificationsForMe Check if a notification received is for me
 It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
@@ -1604,6 +1650,7 @@ a Type, and a source. These are as follows:
 	TYPE       	SOURCE				DESCRIPTION
 	"default"	recipient user ID	A message with no association
 	"request"	sender user ID		A channel request has been received
+	"reset"	    sender user ID		A channel reset has been received
 	"confirm"	sender user ID		A channel request has been accepted
 	"silent"	sender user ID		A message which should not be notified on
 	"e2e"		sender user ID		reception of an E2E message
@@ -1618,8 +1665,17 @@ FOUNDATION_EXPORT BindingsManyNotificationForMeReport* _Nullable BindingsNotific
  */
 FOUNDATION_EXPORT void BindingsRegisterLogWriter(id<BindingsLogWriter> _Nullable writer);
 
-// skipped function RestoreContactsFromBackup with unsupported parameter or return types
-
+/**
+ * RestoreContactsFromBackup takes as input the jason output of the
+`NewClientFromBackup` function, unmarshals it into IDs, looks up
+each ID in user discovery, and initiates a session reset request.
+This function will not return until every id in the list has been sent a
+request. It should be called again and again until it completes.
+xxDK users should not use this function. This function is used by
+the mobile phone apps and are not intended to be part of the xxDK. It
+should be treated as internal functions specific to the phone apps.
+ */
+FOUNDATION_EXPORT BindingsRestoreContactsReport* _Nullable BindingsRestoreContactsFromBackup(NSData* _Nullable backupPartnerIDs, BindingsClient* _Nullable client, BindingsUserDiscovery* _Nullable udManager, id<BindingsLookupCallback> _Nullable lookupCB, id<BindingsRestoreContactsUpdater> _Nullable updatesCb);
 
 /**
  * ResumeBackup starts the backup processes back up with a new callback after it
@@ -1678,7 +1734,7 @@ FOUNDATION_EXPORT BOOL BindingsUpdateCommonErrors(NSString* _Nullable jsonFile,
 
 @class BindingsAuthRequestCallback;
 
-@class BindingsAuthResetCallback;
+@class BindingsAuthResetNotificationCallback;
 
 @class BindingsClientError;
 
@@ -1750,7 +1806,7 @@ request
  * AuthRequestCallback notifies the register whenever they receive an auth
 request
  */
-@interface BindingsAuthResetCallback : NSObject <goSeqRefInterface, BindingsAuthResetCallback> {
+@interface BindingsAuthResetNotificationCallback : NSObject <goSeqRefInterface, BindingsAuthResetNotificationCallback> {
 }
 @property(strong, readonly) _Nonnull id _ref;
 
diff --git a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 0fa572d8e1c912a0a5c75c9c9cabc13c6e36157c..d7628f469871f2e1ed53e79524cddf88345b8ac8 100644
--- a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -10,6 +10,24 @@
           "version": "0.20200225.4"
         }
       },
+      {
+        "package": "Alamofire",
+        "repositoryURL": "https://github.com/Alamofire/Alamofire.git",
+        "state": {
+          "branch": null,
+          "revision": "f82c23a8a7ef8dc1a49a8bfc6a96883e79121864",
+          "version": "5.5.0"
+        }
+      },
+      {
+        "package": "AppAuth",
+        "repositoryURL": "https://github.com/openid/AppAuth-iOS.git",
+        "state": {
+          "branch": null,
+          "revision": "01131d68346c8ae552961c768d583c715fbe1410",
+          "version": "1.4.0"
+        }
+      },
       {
         "package": "BoringSSL-GRPC",
         "repositoryURL": "https://github.com/firebase/boringssl-SwiftPM.git",
@@ -64,6 +82,15 @@
           "version": "1.2.0"
         }
       },
+      {
+        "package": "FilesProvider",
+        "repositoryURL": "https://github.com/amosavian/FileProvider.git",
+        "state": {
+          "branch": null,
+          "revision": "abf68a62541a4193c8d106367ddb3648e8ab693f",
+          "version": "0.26.0"
+        }
+      },
       {
         "package": "Firebase",
         "repositoryURL": "https://github.com/firebase/firebase-ios-sdk.git",
@@ -73,6 +100,15 @@
           "version": "8.10.0"
         }
       },
+      {
+        "package": "GoogleAPIClientForREST",
+        "repositoryURL": "https://github.com/google/google-api-objectivec-client-for-rest",
+        "state": {
+          "branch": null,
+          "revision": "22e0bb02729d60db396e8b90d8189313cd86ba53",
+          "version": "1.6.0"
+        }
+      },
       {
         "package": "GoogleAppMeasurement",
         "repositoryURL": "https://github.com/google/GoogleAppMeasurement.git",
@@ -91,6 +127,15 @@
           "version": "9.1.2"
         }
       },
+      {
+        "package": "GoogleSignIn",
+        "repositoryURL": "https://github.com/google/GoogleSignIn-iOS",
+        "state": {
+          "branch": null,
+          "revision": "60ca2bfd218ccb194a746a79b41d9d50eb7e3af0",
+          "version": "6.1.0"
+        }
+      },
       {
         "package": "GoogleUtilities",
         "repositoryURL": "https://github.com/google/GoogleUtilities.git",
@@ -127,6 +172,15 @@
           "version": "1.7.0"
         }
       },
+      {
+        "package": "GTMAppAuth",
+        "repositoryURL": "https://github.com/google/GTMAppAuth.git",
+        "state": {
+          "branch": null,
+          "revision": "40f4103fb52109032c05599a0c39ad43edbdf80a",
+          "version": "1.2.2"
+        }
+      },
       {
         "package": "KeychainAccess",
         "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess",
@@ -271,6 +325,15 @@
           "version": "1.9.5"
         }
       },
+      {
+        "package": "SwiftyDropbox",
+        "repositoryURL": "https://github.com/dropbox/SwiftyDropbox.git",
+        "state": {
+          "branch": null,
+          "revision": "7af87d903be1cf0af0e76e0394d992943055894e",
+          "version": "8.2.1"
+        }
+      },
       {
         "package": "xctest-dynamic-overlay",
         "repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",