Skip to main content
Version: 5.0

Handling Application Startup

Overview

When we created the application for the getting started tutorial we made sure Identity Vault was properly initialized before we used it. However, we just jumped right into the first tab of the main part of the app. This is not a realistic experience for our users. Let's implement something more realistic.

The Login Page

Our application currently just starts right up in the application itself and we have a button that the user can press to store the authentication information in the vault. This is not realistic. Our application should have a page where the user logs in.

In our case, this will still just be a button that the user presses to fake a log in, but we are getting a step closer to an actual flow by having the login page.

The Startup Flow

When our application starts, the session can be in one of the following states:

  1. Locked:
    1. With valid authentication tokens.
    2. With invalid authentication tokens.
  2. Not logged in.

If the application is locked, the application shall give the user the opportunity to unlock the vault. If the unlock fails, the user shall be given the option to either try again or to clear the session data and log in again.

If the user unlocks the vault and the resulting authentication information is valid, the first tab shall be loaded. For our tutorial application, if we have session data the session is, by definition, valid.

If the user unlocks the vault and the resulting authentication information is expired, the login page shall be loaded. Having expired or otherwise invalid authentication information is not technically possible in our tutorial application, but we will code for it none the less.

If the user is not logged in, the login page shall be loaded.

We will build upon the application we created in the getting started tutorial in order to implement a basic application startup workflow.

Let's Code

As mentioned previously, this tutorial builds upon the application created when doing the getting started tutorial. If you have the code from when you performed that tutorial, then you are good to go. If you need the code you can make a copy from our GitHub repository.

Generate New Pages

In order to implement our startup and authentication strategies, we need to have a LoginPage. We will also replace the "default" page (currently the Tab1Page) with a StartPage that will contain our startup logic.

Generate these pages.

terminal

_10
ionic generate page login
_10
ionic generate page start

Update Routes

With the new pages in place, the routing needs to be fixed. The application's routing scheme has two levels: a base page level and a sub-page level. As such, each of our routes has one of the following formats: /base-page or /base-page/sub-page.

At the base page level, we want to have three different pages: TabsPage, LoginPage, and StartPage. We also want the default route (/) to be the StartPage. Update the src/app/app.routes.ts file to:

  • Define the /tabs route.
  • Define the /login route.
  • Define the /start route.
  • Create a redirect from / to /start.
src/app/app.routes.ts

_21
import { Routes } from '@angular/router';
_21
_21
export const routes: Routes = [
_21
{
_21
path: '',
_21
redirectTo: '/start',
_21
pathMatch: 'full',
_21
},
_21
{
_21
path: 'tabs',
_21
loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
_21
},
_21
{
_21
path: 'login',
_21
loadComponent: () => import('./login/login.page').then((m) => m.LoginPage),
_21
},
_21
{
_21
path: 'start',
_21
loadComponent: () => import('./start/start.page').then((m) => m.StartPage),
_21
},
_21
];

The TabsPage (route: /tabs) has sub-pages. The sub-pages are already set up, but we need to make the following adjustments to src/app/tabs/tabs.routes.ts:

  • Remove the redirect for / since it was moved to src/app/app.routes.ts.
  • Change the main path from tabs (which is now defined in src/app/app.routes.ts) to an empty string.
src/app/tabs/tabs.routes.ts

_28
import { Routes } from '@angular/router';
_28
import { TabsPage } from './tabs.page';
_28
_28
export const routes: Routes = [
_28
{
_28
path: '',
_28
component: TabsPage,
_28
children: [
_28
{
_28
path: 'tab1',
_28
loadComponent: () => import('../tab1/tab1.page').then((m) => m.Tab1Page),
_28
},
_28
{
_28
path: 'tab2',
_28
loadComponent: () => import('../tab2/tab2.page').then((m) => m.Tab2Page),
_28
},
_28
{
_28
path: 'tab3',
_28
loadComponent: () => import('../tab3/tab3.page').then((m) => m.Tab3Page),
_28
},
_28
{
_28
path: '',
_28
redirectTo: '/tabs/tab1',
_28
pathMatch: 'full',
_28
},
_28
],
_28
},
_28
];

The AuthenticationService

Part of our startup strategy involves authentication. We will not really be performing authentication, but we will add the service so that we have the infrastructure in place so we can later add authentication via a solution such as Auth Connect.

Terminal

_10
ionic generate service core/authentication

Generate the authentication service.

Terminal
src/app/core/authentication.service.ts

_10
import { Injectable } from '@angular/core';
_10
_10
@Injectable({
_10
providedIn: 'root',
_10
})
_10
export class AuthenticationService {
_10
constructor() {}
_10
}

An empty service is created.

Terminal
src/app/core/authentication.service.ts

_10
import { Injectable } from '@angular/core';
_10
import { SessionVaultService } from './session-vault.service';
_10
_10
@Injectable({
_10
providedIn: 'root',
_10
})
_10
export class AuthenticationService {
_10
constructor(private sessionVault: SessionVaultService) {}
_10
}

Inject the SessionVaultService.

Terminal
src/app/core/authentication.service.ts

_19
import { Injectable } from '@angular/core';
_19
import { SessionVaultService } from './session-vault.service';
_19
_19
@Injectable({
_19
providedIn: 'root',
_19
})
_19
export class AuthenticationService {
_19
constructor(private sessionVault: SessionVaultService) {}
_19
_19
async login(): Promise<void> {
_19
this.sessionVault.storeSession({
_19
email: 'test@ionic.io',
_19
firstName: 'Tessa',
_19
lastName: 'Testsmith',
_19
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_19
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_19
});
_19
}
_19
}

The user needs to be able to log in. Since we do not yet have an authentication strategy, we will store a fake session.

Terminal
src/app/core/authentication.service.ts

_23
import { Injectable } from '@angular/core';
_23
import { SessionVaultService } from './session-vault.service';
_23
_23
@Injectable({
_23
providedIn: 'root',
_23
})
_23
export class AuthenticationService {
_23
constructor(private sessionVault: SessionVaultService) {}
_23
_23
async login(): Promise<void> {
_23
this.sessionVault.storeSession({
_23
email: 'test@ionic.io',
_23
firstName: 'Tessa',
_23
lastName: 'Testsmith',
_23
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_23
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_23
});
_23
}
_23
_23
async logout(): Promise<void> {
_23
this.sessionVault.clearSession();
_23
}
_23
}

For the logout(), just clear the stored session.

Terminal
src/app/core/authentication.service.ts

_28
import { Injectable } from '@angular/core';
_28
import { SessionVaultService } from './session-vault.service';
_28
_28
@Injectable({
_28
providedIn: 'root',
_28
})
_28
export class AuthenticationService {
_28
constructor(private sessionVault: SessionVaultService) {}
_28
_28
async login(): Promise<void> {
_28
this.sessionVault.storeSession({
_28
email: 'test@ionic.io',
_28
firstName: 'Tessa',
_28
lastName: 'Testsmith',
_28
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_28
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_28
});
_28
}
_28
_28
async logout(): Promise<void> {
_28
this.sessionVault.clearSession();
_28
}
_28
_28
async isAuthenticated(): Promise<boolean> {
_28
const session = await this.sessionVault.getSession();
_28
return !!session;
_28
}
_28
}

To determine if the user is authenticated, check for a stored session.

Generate the authentication service.

An empty service is created.

Inject the SessionVaultService.

The user needs to be able to log in. Since we do not yet have an authentication strategy, we will store a fake session.

For the logout(), just clear the stored session.

To determine if the user is authenticated, check for a stored session.

Terminal

_10
ionic generate service core/authentication

We now have an AuthenticationService that we can use in the rest of our app. We also have a service that we can update to add our actual authentication services using a solution such as Auth Connect.

The LoginPage

The login page simply includes a "Login" button.

src/app/login/login.page.html

_21
<ion-header [translucent]="true">
_21
<ion-toolbar>
_21
<ion-title>Login</ion-title>
_21
</ion-toolbar>
_21
</ion-header>
_21
_21
<ion-content [fullscreen]="true">
_21
<ion-header collapse="condense">
_21
<ion-toolbar>
_21
<ion-title size="large">Login</ion-title>
_21
</ion-toolbar>
_21
</ion-header>
_21
_21
<ion-list>
_21
<ion-item>
_21
<ion-label>
_21
<ion-button expand="block" (click)="login()">Login</ion-button>
_21
</ion-label>
_21
</ion-item>
_21
</ion-list>
_21
</ion-content>

When the button is pressed the following tasks are performed:

  • Attempt to log in.
  • If the login succeeds, go to the Tab1Page.
  • If the login fails we will just log it for now. When actual authentication is implemented this may be a good place to display a "Login Failed" message, but that is beyond the scope of this tutorial.
src/app/login/login.page.ts

_36
import { Component } from '@angular/core';
_36
import {
_36
IonButton,
_36
IonContent,
_36
IonHeader,
_36
IonItem,
_36
IonLabel,
_36
IonList,
_36
IonTitle,
_36
IonToolbar,
_36
NavController,
_36
} from '@ionic/angular/standalone';
_36
import { AuthenticationService } from '../core/authentication.service';
_36
_36
@Component({
_36
selector: 'app-login',
_36
templateUrl: './login.page.html',
_36
styleUrls: ['./login.page.scss'],
_36
standalone: true,
_36
imports: [IonButton, IonContent, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar],
_36
})
_36
export class LoginPage {
_36
constructor(
_36
private navController: NavController,
_36
private authentication: AuthenticationService,
_36
) {}
_36
_36
async login() {
_36
try {
_36
await this.authentication.login();
_36
this.navController.navigateRoot(['tabs', 'tab1']);
_36
} catch (err: unknown) {
_36
console.error('Failed to log in', err);
_36
}
_36
}
_36
}

Update the SessionVaultService

The startup logic needs to determine if the vault is currently locked and provide a mechanism to unlock the vault if it is locked. Update the SessionVaultService to provide unlock() and isLocked() methods.

src/app/code/session-vault.service.ts

_94
import { Injectable } from '@angular/core';
_94
import {
_94
BrowserVault,
_94
DeviceSecurityType,
_94
IdentityVaultConfig,
_94
Vault,
_94
VaultType,
_94
} from '@ionic-enterprise/identity-vault';
_94
import { Session } from '../models/session';
_94
import { VaultFactory } from './vault.factory';
_94
import { Observable, Subject } from 'rxjs';
_94
_94
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_94
_94
@Injectable({
_94
providedIn: 'root',
_94
})
_94
export class SessionVaultService {
_94
private lockedSubject: Subject<boolean>;
_94
this.vault.config?.type !== VaultType.SecureStorage &&

In the isLocked() method, we are ignoring the actual state for SecureStorage or InMemory type vaults because Identity Vault will report them as "locked" even though they logically cannot lock. This is a long standing quirk with Identity Vault that would be a breaking change to fix.

The StartPage

We will start with this requirement: If the unlock fails, the user shall be given the option to either try again or to clear the session data and log in again.

This may seem like an odd place to start, but it is the only requirement that involves the look and feel of the page, so let's get that established first.

src/app/start/start.page.html

_14
<ion-content class="ion-padding">
_14
<ion-list *ngIf="showUnlock">
_14
<ion-item>
_14
<ion-label>
_14
<ion-button expand="block" (click)="performUnlockFlow()">Unlock</ion-button>
_14
</ion-label>
_14
</ion-item>
_14
<ion-item>
_14
<ion-label>
_14
<ion-button expand="block" (click)="redoLogin()">Redo Login</ion-button>
_14
</ion-label>
_14
</ion-item>
_14
</ion-list>
_14
</ion-content>

We are only conditionally showing the "Unlock" and "Redo Login" buttons. For now, we will hard code the condition to not display these buttons. We also removed the header, toolbar and title as we want this page to be minimal. Remove those components, add our new ones, and create empty methods for the bound click handlers.

src/app/start/start.page.ts

_22
import { CommonModule } from '@angular/common';
_22
import { Component, OnInit } from '@angular/core';
_22
import { IonButton, IonContent, IonItem, IonLabel, IonList } from '@ionic/angular/standalone';
_22
_22
@Component({
_22
selector: 'app-start',
_22
templateUrl: './start.page.html',
_22
styleUrls: ['./start.page.scss'],
_22
standalone: true,
_22
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_22
})
_22
export class StartPage implements OnInit {
_22
showUnlock: boolean = false;
_22
_22
constructor() {}
_22
_22
async ngOnInit() {}
_22
_22
async performUnlockFlow() {}
_22
_22
async redoLogin() {}
_22
}

With the basics in place, let's implement the rest of the logic.

src/app/start/start.page.ts

_31
import { CommonModule } from '@angular/common';
_31
import { Component, OnInit } from '@angular/core';
_31
import { IonButton, IonContent, IonItem, IonLabel, IonList } from '@ionic/angular/standalone';
_31
_31
@Component({
_31
selector: 'app-start',
_31
templateUrl: './start.page.html',
_31
styleUrls: ['./start.page.scss'],
_31
standalone: true,
_31
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_31
})
_31
export class StartPage implements OnInit {
_31
showUnlock: boolean = false;
_31
_31
constructor() {}
_31
_31
async ngOnInit() {
_31
await this.performUnlockFlow();
_31
}
_31
_31
async performUnlockFlow() {
_31
await this.attemptUnlock();
_31
await this.attemptNavigation();
_31
}
_31
_31
async redoLogin() {}
_31
_31
private async attemptNavigation(): Promise<void> {}
_31
_31
private async attemptUnlock(): Promise<void> {}
_31
}

For the unlock flow, we will first attempt an unlock, and then see if we can navigate. Perform this flow when the user navigates to this page.

src/app/start/start.page.ts

_40
import { CommonModule } from '@angular/common';
_40
import { Component, OnInit } from '@angular/core';
_40
import { IonButton, IonContent, IonItem, IonLabel, IonList } from '@ionic/angular/standalone';
_40
import { SessionVaultService } from '../core/session-vault.service';
_40
_40
@Component({
_40
selector: 'app-start',
_40
templateUrl: './start.page.html',
_40
styleUrls: ['./start.page.scss'],
_40
standalone: true,
_40
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_40
})
_40
export class StartPage implements OnInit {
_40
showUnlock: boolean = false;
_40
_40
constructor(private sessionVault: SessionVaultService) {}
_40
_40
async ngOnInit() {
_40
await this.performUnlockFlow();
_40
}
_40
_40
async performUnlockFlow() {
_40
await this.attemptUnlock();
_40
await this.attemptNavigation();
_40
}
_40
_40
async redoLogin() {}
_40
_40
private async attemptNavigation(): Promise<void> {}
_40
_40
private async attemptUnlock(): Promise<void> {
_40
if (await this.sessionVault.isLocked()) {
_40
try {
_40
await this.sessionVault.unlock();
_40
} catch (err: unknown) {
_40
this.showUnlock = true;
_40
}
_40
}
_40
}
_40
}

We will only attempt the unlock operation if the vault is actually locked. Try to unlock the vault. If the unlock fails, set the "show" flag so the user can try again or give up and go back to the login step.

src/app/start/start.page.ts

_53
import { CommonModule } from '@angular/common';
_53
import { Component, OnInit } from '@angular/core';
_53
import { IonButton, IonContent, IonItem, IonLabel, IonList, NavController } from '@ionic/angular/standalone';
_53
import { AuthenticationService } from '../core/authentication.service';
_53
import { SessionVaultService } from '../core/session-vault.service';
_53
_53
@Component({
_53
selector: 'app-start',
_53
templateUrl: './start.page.html',
_53
styleUrls: ['./start.page.scss'],
_53
standalone: true,
_53
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_53
})
_53
export class StartPage implements OnInit {
_53
showUnlock: boolean = false;
_53
_53
constructor(
_53
private authentication: AuthenticationService,
_53
private navController: NavController,
_53
private sessionVault: SessionVaultService,
_53
) {}
_53
_53
async ngOnInit() {
_53
await this.performUnlockFlow();
_53
}
_53
_53
async performUnlockFlow() {
_53
await this.attemptUnlock();
_53
await this.attemptNavigation();
_53
}
_53
_53
async redoLogin() {}
_53
_53
private async attemptNavigation(): Promise<void> {
_53
if (!(await this.sessionVault.isLocked())) {
_53
if (await this.authentication.isAuthenticated()) {
_53
this.navController.navigateRoot(['tabs', 'tab1']);
_53
} else {
_53
this.navController.navigateRoot(['login']);
_53
}
_53
}
_53
}
_53
_53
private async attemptUnlock(): Promise<void> {
_53
if (await this.sessionVault.isLocked()) {
_53
try {
_53
await this.sessionVault.unlock();
_53
} catch (err: unknown) {
_53
this.showUnlock = true;
_53
}
_53
}
_53
}
_53
}

If the user succeeded in unlocking the vault, determine if we should navigate to the LoginPage or the Tab1Page based on the current authentication status.

src/app/start/start.page.ts

_56
import { CommonModule } from '@angular/common';
_56
import { Component, OnInit } from '@angular/core';
_56
import { IonButton, IonContent, IonItem, IonLabel, IonList, NavController } from '@ionic/angular/standalone';
_56
import { AuthenticationService } from '../core/authentication.service';
_56
import { SessionVaultService } from '../core/session-vault.service';
_56
_56
@Component({
_56
selector: 'app-start',
_56
templateUrl: './start.page.html',
_56
styleUrls: ['./start.page.scss'],
_56
standalone: true,
_56
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_56
})
_56
export class StartPage implements OnInit {
_56
showUnlock: boolean = false;
_56
_56
constructor(
_56
private authentication: AuthenticationService,
_56
private navController: NavController,
_56
private sessionVault: SessionVaultService,
_56
) {}
_56
_56
async ngOnInit() {
_56
await this.performUnlockFlow();
_56
}
_56
_56
async performUnlockFlow() {
_56
await this.attemptUnlock();
_56
await this.attemptNavigation();
_56
}
_56
_56
async redoLogin() {
_56
await this.authentication.logout();
_56
this.navController.navigateRoot(['login']);
_56
}
_56
_56
private async attemptNavigation(): Promise<void> {
_56
if (!(await this.sessionVault.isLocked())) {
_56
if (await this.authentication.isAuthenticated()) {
_56
this.navController.navigateRoot(['tabs', 'tab1']);
_56
} else {
_56
this.navController.navigateRoot(['login']);
_56
}
_56
}
_56
}
_56
_56
private async attemptUnlock(): Promise<void> {
_56
if (await this.sessionVault.isLocked()) {
_56
try {
_56
await this.sessionVault.unlock();
_56
} catch (err: unknown) {
_56
this.showUnlock = true;
_56
}
_56
}
_56
}
_56
}

If the user chooses to redo the login, logout and navigate to the LoginPage.

For the unlock flow, we will first attempt an unlock, and then see if we can navigate. Perform this flow when the user navigates to this page.

We will only attempt the unlock operation if the vault is actually locked. Try to unlock the vault. If the unlock fails, set the "show" flag so the user can try again or give up and go back to the login step.

If the user succeeded in unlocking the vault, determine if we should navigate to the LoginPage or the Tab1Page based on the current authentication status.

If the user chooses to redo the login, logout and navigate to the LoginPage.

src/app/start/start.page.ts

_31
import { CommonModule } from '@angular/common';
_31
import { Component, OnInit } from '@angular/core';
_31
import { IonButton, IonContent, IonItem, IonLabel, IonList } from '@ionic/angular/standalone';
_31
_31
@Component({
_31
selector: 'app-start',
_31
templateUrl: './start.page.html',
_31
styleUrls: ['./start.page.scss'],
_31
standalone: true,
_31
imports: [CommonModule, IonButton, IonContent, IonItem, IonLabel, IonList],
_31
})
_31
export class StartPage implements OnInit {
_31
showUnlock: boolean = false;
_31
_31
constructor() {}
_31
_31
async ngOnInit() {
_31
await this.performUnlockFlow();
_31
}
_31
_31
async performUnlockFlow() {
_31
await this.attemptUnlock();
_31
await this.attemptNavigation();
_31
}
_31
_31
async redoLogin() {}
_31
_31
private async attemptNavigation(): Promise<void> {}
_31
_31
private async attemptUnlock(): Promise<void> {}
_31
}

One item of note on the redoLogin() code. If we are using an authentication system, we need to craft our logout() method such that it can be called with a locked vault. Crafting the logout as such is beyond the scope of this tutorial.

Redirect on Lock

src/app/app.component.ts

_26
import { Component, OnDestroy } from '@angular/core';
_26
import { IonApp, IonRouterOutlet, NavController } from '@ionic/angular/standalone';
_26
import { Subscription } from 'rxjs';
_26
import { SessionVaultService } from './core/session-vault.service';
_26
_26
@Component({
_26
selector: 'app-root',
_26
templateUrl: 'app.component.html',
_26
standalone: true,
_26
imports: [IonApp, IonRouterOutlet],
_26
})
_26
export class AppComponent implements OnDestroy {
_26
private subscription: Subscription;
_26
_26
constructor(navController: NavController, sessionVault: SessionVaultService) {
_26
this.subscription = sessionVault.locked$.subscribe((lock) => {
_26
if (lock) {
_26
navController.navigateRoot(['/']);
_26
}
_26
});
_26
}
_26
_26
ngOnDestroy() {
_26
this.subscription.unsubscribe();
_26
}
_26
}

Cleanup the Tab1Page

This step is completely optional. The tutorial application will work perfectly fine with the code as-is. There are several items in the Tab1Page that no longer make sense, however, and now is a good time to clean those up. Here is a synopsis of what can be cleaned up:

  • Remove the "Store" button and all code associated with it.
  • Change the "Clear" button to a "Logout" button and update the click handler accordingly.
  • Remove the "Unlock" button and all code associated with it.
  • Remove the code that subscribes to the locked$ observable and clears the displayed session data when the vault is locked. This is no longer needed because we navigate away from the page entirely when the vault locks.

Cleaning this all up is left as an exercise to the reader but we provide the completed code here for you to compare against.

src/app/tab1/tab1.page.html
src/app/tab1/tab1.page.ts

_53
<ion-header [translucent]="true">
_53
<ion-toolbar>
_53
<ion-title> Tab1 </ion-title>
_53
</ion-toolbar>
_53
</ion-header>
_53
_53
<ion-content [fullscreen]="true">
_53
<ion-header collapse="condense">
_53
<ion-toolbar>
_53
<ion-title size="large">Tab1</ion-title>
_53
</ion-toolbar>
_53
</ion-header>
_53
_53
<ion-list>
_53
<ion-item>
_53
<ion-label>
_53
<ion-button expand="block" color="danger" (click)="logout()">Logout</ion-button>
_53
</ion-label>
_53
</ion-item>
_53
<ion-item>
_53
<ion-label>
_53
<ion-button expand="block" color="secondary" (click)="changeUnlockMode('BiometricsWithPasscode')"
_53
>Use Biometrics</ion-button
_53
>
_53
</ion-label>
_53
</ion-item>
_53
<ion-item>
_53
<ion-label>
_53
<ion-button expand="block" color="secondary" (click)="changeUnlockMode('InMemory')">Use In Memory</ion-button>
_53
</ion-label>
_53
</ion-item>
_53
<ion-item>
_53
<ion-label>
_53
<ion-button expand="block" color="secondary" (click)="changeUnlockMode('SecureStorage')"
_53
>Use Secure Storage</ion-button
_53
>
_53
</ion-label>
_53
</ion-item>
_53
<ion-item>
_53
<ion-label>
_53
<ion-button expand="block" color="warning" (click)="lock()">Lock</ion-button>
_53
</ion-label>
_53
</ion-item>
_53
<ion-item>
_53
<div>
_53
<div>{{session?.email}}</div>
_53
<div>{{session?.firstName}} {{session?.lastName}}</div>
_53
<div>{{session?.accessToken}}</div>
_53
<div>{{session?.refreshToken}}</div>
_53
</div>
_53
</ion-item>
_53
</ion-list>
_53
</ion-content>

Next Steps

In this tutorial, we created a good basic application startup workflow. This is an example of a good workflow, but it is not the only potential flow. For example, our application simply navigates to /tabs/tab1 after unlocking the vault. You could, however, store information about the current state of the application and then restore to that state after unlocking the application. Do whatever is right for your application.