App initialization and checks (#223)
* AppDelegate connected * Initialisation of the app and checks in place * cleanup * switch-default purpose commented * checkWalletInitialisation simplified * initialisation -> initialization
This commit is contained in:
parent
18cff600d0
commit
fd7109d1f0
|
@ -20,7 +20,6 @@
|
|||
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */; };
|
||||
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */; };
|
||||
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0826B364170058B01E /* SecantApp.swift */; };
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0A26B364170058B01E /* ContentView.swift */; };
|
||||
0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0C26B364180058B01E /* Assets.xcassets */; };
|
||||
0D4E7A1026B364180058B01E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0F26B364180058B01E /* Preview Assets.xcassets */; };
|
||||
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A1A26B364180058B01E /* secantTests.swift */; };
|
||||
|
@ -92,6 +91,8 @@
|
|||
9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; };
|
||||
9EF8135C27ECC25E0075AF48 /* RecoveryPhraseStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* RecoveryPhraseStorageTests.swift */; };
|
||||
9EF8135D27ECC25E0075AF48 /* UserPreferencesStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */; };
|
||||
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */; };
|
||||
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; };
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
|
||||
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
|
||||
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
|
||||
|
@ -153,7 +154,6 @@
|
|||
0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayStore.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0526B364170058B01E /* secant-testnet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "secant-testnet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantApp.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0C26B364180058B01E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0D4E7A0F26B364180058B01E /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
0D4E7A1126B364180058B01E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -229,6 +229,8 @@
|
|||
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
|
||||
9EF8135A27ECC25E0075AF48 /* RecoveryPhraseStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseStorageTests.swift; sourceTree = "<group>"; };
|
||||
9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageTests.swift; sourceTree = "<group>"; };
|
||||
9EF8135F27F043CC0075AF48 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
9EF8139B27F47AED0075AF48 /* InitializationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationState.swift; sourceTree = "<group>"; };
|
||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
||||
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
|
||||
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
|
||||
|
@ -396,7 +398,6 @@
|
|||
0D1922EB26BDD9A500052649 /* Screens */,
|
||||
0D170A7426BC9B7500EB6A46 /* MockedDependencies */,
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */,
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */,
|
||||
0D4E7A0C26B364180058B01E /* Assets.xcassets */,
|
||||
660558E8270C7A54009D6954 /* Colors.xcassets */,
|
||||
9E37A2B727C8F59F00AE57B3 /* Localizable.strings */,
|
||||
|
@ -511,6 +512,7 @@
|
|||
9E2AC10027D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift */,
|
||||
9E2AC10227DA28200042AA47 /* RecoveryPhraseStorage.swift */,
|
||||
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */,
|
||||
9EF8139B27F47AED0075AF48 /* InitializationState.swift */,
|
||||
);
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
|
@ -746,6 +748,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
F9971A4A27680DC400A2DB75 /* App.swift */,
|
||||
9EF8135F27F043CC0075AF48 /* AppDelegate.swift */,
|
||||
F9971A4B27680DC400A2DB75 /* Views */,
|
||||
);
|
||||
path = App;
|
||||
|
@ -1069,6 +1072,7 @@
|
|||
0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */,
|
||||
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */,
|
||||
669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */,
|
||||
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */,
|
||||
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */,
|
||||
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
|
||||
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
|
||||
|
@ -1109,7 +1113,6 @@
|
|||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */,
|
||||
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */,
|
||||
9E2AC10127D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift in Sources */,
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */,
|
||||
0D354A0926D5A9D000315F45 /* Services.swift in Sources */,
|
||||
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */,
|
||||
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */,
|
||||
|
@ -1143,6 +1146,7 @@
|
|||
F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */,
|
||||
0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */,
|
||||
F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */,
|
||||
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */,
|
||||
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */,
|
||||
6654C7412715A47300901167 /* Onboarding.swift in Sources */,
|
||||
F9C165C42740403600592F76 /* SentView.swift in Sources */,
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 7/29/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
Text("Hello, Zcash!")
|
||||
.font(FontFamily.Zboto.regular.textStyle(.body))
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
|
@ -15,29 +15,37 @@ struct AppState: Equatable {
|
|||
var phraseValidationState: RecoveryPhraseValidationState
|
||||
var phraseDisplayState: RecoveryPhraseDisplayState
|
||||
var route: Route = .welcome
|
||||
var storedWallet: StoredWallet?
|
||||
var appInitializationState: InitializationState = .uninitialized
|
||||
}
|
||||
|
||||
enum AppAction: Equatable {
|
||||
case appDelegate(AppDelegateAction)
|
||||
case checkWalletInitialization
|
||||
case createNewWallet
|
||||
case updateRoute(AppState.Route)
|
||||
case home(HomeAction)
|
||||
case initializeApp
|
||||
case onboarding(OnboardingAction)
|
||||
case phraseDisplay(RecoveryPhraseDisplayAction)
|
||||
case phraseValidation(RecoveryPhraseValidationAction)
|
||||
case updateRoute(AppState.Route)
|
||||
}
|
||||
|
||||
struct AppEnvironment {
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
let walletStorage: RecoveryPhraseStorage
|
||||
}
|
||||
|
||||
extension AppEnvironment {
|
||||
static let live = AppEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
walletStorage: RecoveryPhraseStorage()
|
||||
)
|
||||
|
||||
static let mock = AppEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
mnemonicSeedPhraseProvider: .mock,
|
||||
walletStorage: RecoveryPhraseStorage()
|
||||
)
|
||||
|
@ -82,11 +90,61 @@ extension AppReducer {
|
|||
|
||||
return Effect(value: .phraseValidation(.displayBackedUpPhrase))
|
||||
|
||||
/// Checking presense of stored wallet in the keychain and presense of database files in documents directory.
|
||||
case .checkWalletInitialization:
|
||||
// TODO: Create a dependency to handle database files for the SDK, issue #220 (https://github.com/zcash/secant-ios-wallet/issues/220)
|
||||
let fileManager = FileManager()
|
||||
// TODO: use updated dependency from PR #217 (https://github.com/zcash/secant-ios-wallet/pull/217)
|
||||
let keysPresent = environment.walletStorage.areKeysPresent()
|
||||
|
||||
do {
|
||||
// TODO: use database URL from the same issue #220
|
||||
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||
let dataDatabaseURL = documentsURL.appendingPathComponent("ZcashSDK.defaultDataDbName", isDirectory: false)
|
||||
let attributes = try fileManager.attributesOfItem(atPath: dataDatabaseURL.path)
|
||||
let databaseFilesPresent = attributes.isEmpty
|
||||
|
||||
switch (keysPresent, databaseFilesPresent) {
|
||||
case (false, false):
|
||||
return Effect(value: .updateRoute(.onboarding))
|
||||
.delay(for: 3, scheduler: environment.scheduler)
|
||||
.eraseToEffect()
|
||||
case (false, true):
|
||||
state.appInitializationState = .keysMissing
|
||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
case (true, false), (true, true):
|
||||
return Effect(value: .initializeApp)
|
||||
}
|
||||
} catch CocoaError.fileNoSuchFile, CocoaError.fileReadNoSuchFile {
|
||||
state.appInitializationState = keysPresent ? .filesMissing : .uninitialized
|
||||
} catch {
|
||||
state.appInitializationState = .failed
|
||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
}
|
||||
return .none
|
||||
|
||||
/// Stored wallet is present, database files may or may not be present, trying to initialize app state variables and environments.
|
||||
/// When initialization succeeds user is taken to the home screen.
|
||||
case .initializeApp:
|
||||
do {
|
||||
state.storedWallet = try environment.walletStorage.exportWallet()
|
||||
} catch {
|
||||
state.appInitializationState = .failed
|
||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
return .none
|
||||
}
|
||||
|
||||
state.appInitializationState = .initialized
|
||||
return Effect(value: .updateRoute(.startup))
|
||||
.delay(for: 3, scheduler: environment.scheduler)
|
||||
.eraseToEffect()
|
||||
|
||||
/// Default is meaningful here because there's `routeReducer` handling routes and this reducer is handling only actions. We don't here plenty of unused cases.
|
||||
default:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static let routeReducer = AppReducer { state, action, _ in
|
||||
switch action {
|
||||
case let .updateRoute(route):
|
||||
|
@ -108,6 +166,7 @@ extension AppReducer {
|
|||
case .phraseDisplay(.finishedPressed):
|
||||
state.route = .phraseValidation
|
||||
|
||||
/// Default is meaningful here because there's `appReducer` handling actions and this reducer is handling only routes. We don't here plenty of unused cases.
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -145,6 +204,13 @@ extension AppReducer {
|
|||
typealias AppStore = Store<AppState, AppAction>
|
||||
|
||||
extension AppStore {
|
||||
static var placeholder: AppStore {
|
||||
AppStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .mock
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppViewStore
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 27.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AppDelegateAction: Equatable {
|
||||
case didFinishLaunching
|
||||
}
|
|
@ -58,14 +58,7 @@ struct AppView: View {
|
|||
}
|
||||
case .welcome:
|
||||
WelcomeView()
|
||||
.transition(.opacity)
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
withAnimation(.easeInOut(duration: 1)) {
|
||||
viewStore.send(.updateRoute(.startup))
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear(perform: { viewStore.send(.checkWalletInitialization) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ protocol KeyStoring {
|
|||
/**
|
||||
Check if the wallet representation `StoredWallet` is present in the persistent storage.
|
||||
*/
|
||||
func areKeysPresent() throws -> Bool
|
||||
func areKeysPresent() -> Bool
|
||||
|
||||
/**
|
||||
Update the birthday in the securely stored wallet.
|
||||
|
|
|
@ -6,24 +6,31 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
var appStore: AppStore = .placeholder
|
||||
lazy var appViewStore = ViewStore(
|
||||
appStore.stateless,
|
||||
removeDuplicates: ==
|
||||
)
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
appViewStore.send(.appDelegate(.didFinishLaunching))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct SecantApp: App {
|
||||
var appStore: AppStore = .placeholder
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
AppView(store: appStore)
|
||||
AppView(store: appDelegate.appStore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppStore {
|
||||
static var placeholder: AppStore {
|
||||
AppStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .mock
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// InitializationState.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 30.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum InitializationState: Equatable {
|
||||
case failed
|
||||
case initialized
|
||||
case keysMissing
|
||||
case filesMissing
|
||||
case uninitialized
|
||||
}
|
|
@ -220,7 +220,7 @@ extension RecoveryPhraseStorage: KeyStoring {
|
|||
}
|
||||
}
|
||||
|
||||
func areKeysPresent() throws -> Bool {
|
||||
func areKeysPresent() -> Bool {
|
||||
do {
|
||||
_ = try exportWallet()
|
||||
} catch KeyStoringError.uninitializedWallet {
|
||||
|
|
Loading…
Reference in New Issue