diff --git a/README.md b/README.md
index d6128517..0716a7cb 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ npm start
- [@nativescript/localize](packages/localize/README.md)
- [@nativescript/pdf](packages/pdf/README.md)
- [@nativescript/picker](packages/picker/README.md)
+- [@nativescript/secure-storage](packages/secure-storage/README.md)
- [@nativescript/shared-notification-delegate](packages/shared-notification-delegate/README.md)
- [@nativescript/social-share](packages/social-share/README.md)
- [@nativescript/swift-ui](packages/swift-ui/README.md)
diff --git a/apps/demo-angular/package.json b/apps/demo-angular/package.json
index d48f8d29..90b82134 100644
--- a/apps/demo-angular/package.json
+++ b/apps/demo-angular/package.json
@@ -37,7 +37,8 @@
"@nativescript/swift-ui": "file:../../dist/packages/swift-ui",
"@nativescript/theme-switcher": "file:../../dist/packages/theme-switcher",
"@nativescript/twitter": "file:../../dist/packages/twitter",
- "@nativescript/zip": "file:../../dist/packages/zip"
+ "@nativescript/zip": "file:../../dist/packages/zip",
+ "@nativescript/secure-storage": "file:../../dist/packages/secure-storage"
},
"devDependencies": {
"@nativescript/android": "~8.5.0",
diff --git a/apps/demo-angular/src/app-routing.module.ts b/apps/demo-angular/src/app-routing.module.ts
index cefc7451..a33e9734 100644
--- a/apps/demo-angular/src/app-routing.module.ts
+++ b/apps/demo-angular/src/app-routing.module.ts
@@ -37,6 +37,7 @@ const routes: Routes = [
{ path: 'localize', loadChildren: () => import('./plugin-demos/localize.module').then((m) => m.LocalizeModule) },
{ path: 'pdf', loadChildren: () => import('./plugin-demos/pdf.module').then((m) => m.PdfModule) },
{ path: 'picker', loadChildren: () => import('./plugin-demos/picker.module').then((m) => m.PickerModule) },
+ { path: 'secure-storage', loadChildren: () => import('./plugin-demos/secure-storage.module').then((m) => m.SecureStorageModule) },
{ path: 'shared-notification-delegate', loadChildren: () => import('./plugin-demos/shared-notification-delegate.module').then((m) => m.SharedNotificationDelegateModule) },
{ path: 'social-share', loadChildren: () => import('./plugin-demos/social-share.module').then((m) => m.SocialShareModule) },
{ path: 'swift-ui', loadChildren: () => import('./plugin-demos/swift-ui.module').then((m) => m.SwiftUiModule) },
diff --git a/apps/demo-angular/src/home.component.ts b/apps/demo-angular/src/home.component.ts
index 863f9cb6..13e83501 100644
--- a/apps/demo-angular/src/home.component.ts
+++ b/apps/demo-angular/src/home.component.ts
@@ -96,6 +96,9 @@ export class HomeComponent {
{
name: 'picker',
},
+ {
+ name: 'secure-storage',
+ },
{
name: 'shared-notification-delegate',
},
diff --git a/apps/demo-angular/src/plugin-demos/secure-storage.component.html b/apps/demo-angular/src/plugin-demos/secure-storage.component.html
new file mode 100644
index 00000000..99a8d1eb
--- /dev/null
+++ b/apps/demo-angular/src/plugin-demos/secure-storage.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/demo-angular/src/plugin-demos/secure-storage.component.scss b/apps/demo-angular/src/plugin-demos/secure-storage.component.scss
new file mode 100644
index 00000000..e0e47d56
--- /dev/null
+++ b/apps/demo-angular/src/plugin-demos/secure-storage.component.scss
@@ -0,0 +1,34 @@
+.message {
+ color: #666;
+ font-size: 16;
+ padding: 20;
+}
+
+button {
+ color: #ffffff;
+ background-color: #6494aa;
+ padding: 6;
+ margin: 8 24;
+ font-size: 14;
+ border-radius: 4;
+}
+
+.button-group-first {
+ margin-top: 24;
+}
+
+.button-a {
+ background-color: cornflowerblue;
+}
+
+.button-b {
+ background-color: forestgreen;
+}
+
+.button-c {
+ background-color: orange;
+}
+
+.button-d {
+ background-color: red;
+}
diff --git a/apps/demo-angular/src/plugin-demos/secure-storage.component.ts b/apps/demo-angular/src/plugin-demos/secure-storage.component.ts
new file mode 100644
index 00000000..1d66c4eb
--- /dev/null
+++ b/apps/demo-angular/src/plugin-demos/secure-storage.component.ts
@@ -0,0 +1,17 @@
+import { Component, NgZone } from '@angular/core';
+import { DemoSharedSecureStorage } from '@demo/shared';
+
+@Component({
+ selector: 'demo-secure-storage',
+ templateUrl: 'secure-storage.component.html',
+ styleUrls: ['secure-storage.component.scss'],
+})
+export class SecureStorageComponent {
+ demoShared: DemoSharedSecureStorage;
+
+ constructor(private _ngZone: NgZone) {}
+
+ ngOnInit() {
+ this.demoShared = new DemoSharedSecureStorage();
+ }
+}
diff --git a/apps/demo-angular/src/plugin-demos/secure-storage.module.ts b/apps/demo-angular/src/plugin-demos/secure-storage.module.ts
new file mode 100644
index 00000000..1628214a
--- /dev/null
+++ b/apps/demo-angular/src/plugin-demos/secure-storage.module.ts
@@ -0,0 +1,10 @@
+import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
+import { NativeScriptCommonModule, NativeScriptRouterModule } from '@nativescript/angular';
+import { SecureStorageComponent } from './secure-storage.component';
+
+@NgModule({
+ imports: [NativeScriptCommonModule, NativeScriptRouterModule.forChild([{ path: '', component: SecureStorageComponent }])],
+ declarations: [SecureStorageComponent],
+ schemas: [NO_ERRORS_SCHEMA],
+})
+export class SecureStorageModule {}
diff --git a/apps/demo/package.json b/apps/demo/package.json
index c2110ae2..c7d5a7f3 100644
--- a/apps/demo/package.json
+++ b/apps/demo/package.json
@@ -40,7 +40,8 @@
"@nativescript/swift-ui": "file:../../packages/swift-ui",
"@nativescript/theme-switcher": "file:../../packages/theme-switcher",
"@nativescript/twitter": "file:../../packages/twitter",
- "@nativescript/zip": "file:../../packages/zip"
+ "@nativescript/zip": "file:../../packages/zip",
+ "@nativescript/secure-storage": "file:../../packages/secure-storage"
},
"devDependencies": {
"@nativescript/android": "~8.5.0",
diff --git a/apps/demo/src/main-page.xml b/apps/demo/src/main-page.xml
index f68418ec..9ca01d76 100644
--- a/apps/demo/src/main-page.xml
+++ b/apps/demo/src/main-page.xml
@@ -41,6 +41,7 @@
+
diff --git a/apps/demo/src/plugin-demos/secure-storage.ts b/apps/demo/src/plugin-demos/secure-storage.ts
new file mode 100644
index 00000000..a9248e43
--- /dev/null
+++ b/apps/demo/src/plugin-demos/secure-storage.ts
@@ -0,0 +1,136 @@
+import { EventData, Observable, Page } from '@nativescript/core';
+import { DemoSharedSecureStorage } from '@demo/shared';
+import { SecureStorage } from '@nativescript/secure-storage';
+
+export function navigatingTo(args: EventData) {
+ const page = args.object;
+ page.bindingContext = new DemoModel();
+}
+
+export class DemoModel extends DemoSharedSecureStorage {
+ private secureStorage: SecureStorage;
+
+ constructor() {
+ super();
+ this.secureStorage = new SecureStorage();
+ }
+
+ public doGet() {
+ this.secureStorage
+ .get({
+ key: 'foo',
+ })
+ .then(
+ (value) => {
+ console.log('Value: ' + value);
+ this.set('lastRetrievedValue', value === null ? '(no value set)' : value);
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ public doGetSync() {
+ console.log('go');
+ const value = this.secureStorage.getSync({
+ key: 'foo',
+ });
+ this.set('lastRetrievedValue', value === null ? '(no value set)' : value);
+ }
+
+ public doSet() {
+ this.secureStorage
+ .set({
+ key: 'foo',
+ value: 'I was set at ' + new Date(),
+ })
+ .then(
+ (success) => {
+ console.log('Successfully set a value? ' + success);
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ public doSetSync() {
+ const success = this.secureStorage.setSync({
+ key: 'foo',
+ value: 'I was set at ' + new Date(),
+ });
+ console.log('Successfully set a value? ' + success);
+ }
+
+ public doRemove() {
+ this.secureStorage
+ .remove({
+ key: 'foo',
+ })
+ .then(
+ (success) => {
+ console.log('Successfully removed a value? ' + success);
+ this.set('lastRetrievedValue', '');
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ public doRemoveSync() {
+ this.secureStorage.removeSync({
+ key: 'foo',
+ });
+ this.set('lastRetrievedValue', '');
+ }
+
+ public doRemoveAll() {
+ this.secureStorage.removeAll().then(
+ (success) => {
+ console.log('Successfully removed all values? ' + success);
+ this.set('lastRetrievedValue', '');
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ public doRemoveAllSync() {
+ this.secureStorage.removeAllSync();
+ this.set('lastRetrievedValue', '');
+ }
+
+ public doClearAllOnFirstRunSync() {
+ const res: boolean = this.secureStorage.clearAllOnFirstRunSync();
+ if (res) {
+ console.log('Cleared');
+ this.set('lastRetrievedValue', '');
+ } else {
+ alert('Is not the first run ! \n use `removeAllSync` or `removeAll` ;-)');
+ }
+ }
+
+ public doClearAllOnFirstRun() {
+ this.secureStorage.clearAllOnFirstRun().then((res) => {
+ if (res) {
+ console.log('Cleared');
+ this.set('lastRetrievedValue', '');
+ } else {
+ alert('Is not the first run ! \n use `removeAllSync` or `removeAll` ;-)');
+ }
+ });
+ }
+
+ public doIsFirstRun() {
+ this.secureStorage.isFirstRun().then((isFirst) => {
+ this.set('isFirstRun', isFirst);
+ });
+ }
+
+ public doIsFirstRunSync() {
+ this.set('isFirstRun', this.secureStorage.isFirstRunSync());
+ }
+}
diff --git a/apps/demo/src/plugin-demos/secure-storage.xml b/apps/demo/src/plugin-demos/secure-storage.xml
new file mode 100644
index 00000000..e49d4c73
--- /dev/null
+++ b/apps/demo/src/plugin-demos/secure-storage.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/secure-storage/.eslintrc.json b/packages/secure-storage/.eslintrc.json
new file mode 100644
index 00000000..be41074b
--- /dev/null
+++ b/packages/secure-storage/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["../../.eslintrc.json"],
+ "ignorePatterns": ["!**/*", "node_modules/**/*"],
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.js", "*.jsx"],
+ "rules": {}
+ }
+ ]
+}
diff --git a/packages/secure-storage/README.md b/packages/secure-storage/README.md
new file mode 100644
index 00000000..b269476f
--- /dev/null
+++ b/packages/secure-storage/README.md
@@ -0,0 +1,90 @@
+# @nativescript/secure-storage
+
+```javascript
+ns plugin add @nativescript/secure-storage
+```
+
+## Usage
+
+# Changelog
+
+## [2.6.0](https://github.com/EddyVerbruggen/nativescript-secure-storage/milestone/10?closed=1) (2019-09-19)
+See the link above.
+
+
+## [2.5.0](https://github.com/EddyVerbruggen/nativescript-secure-storage/milestone/9?closed=1) (2019-07-10)
+
+- Add the Ability to clear data on the "first run" of the App
+
+
+## [2.4.0](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.4.0) (2019-02-08)
+
+- Change android gradle config to use "implementation" instead of "compile"
+- add NativeScript VueJs Demo
+- Update to refer to TNS version 5.1
+- align plugin strcture with NativeScript plugin seed
+
+[Milestone](https://github.com/EddyVerbruggen/nativescript-secure-storage/milestone/8?closed=1)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.3.0...2.4.0))
+
+## [2.3.0](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.3.0) (2018-05-18)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.2.2...2.3.0)
+
+**Implemented enhancements:**
+
+- Increase the security for when the keys are accessible on iOS [\#19](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/19)
+
+## [2.2.2](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.2.2) (2018-02-01)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.2.0...2.2.2)
+
+**Implemented enhancements:**
+
+- Catch errors when trying to remove keys that don't exist in iOS. [\#15](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/15)
+- Bump SAMKeychain Pod dependency [\#16](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/16)
+
+## [2.2.0](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.2.0) (2017-08-11)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.1.1...2.2.0)
+
+**Implemented enhancements:**
+
+- return without promise [\#9](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/9)
+
+## [2.1.1](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.1.0) (2017-07-01)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.1.0...2.1.1)
+
+**Implemented enhancements:**
+
+- Not working in iOS simulator. Gives "found no keychain client entitlements" [\#10](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/10)
+
+## [2.1.0](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.1.0) (2016-12-23)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.0.2...2.1.0)
+
+**Closed issues:**
+
+- Limit storage to Strings [\#5](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/5)
+
+## [2.0.2](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.0.2) (2016-12-14)
+[Full Changelog](https://github.com/eddyverbruggen/nativescript-secure-storage/compare/2.0.0...2.0.2)
+
+**Implemented enhancements:**
+
+- Android Implementation [\#1](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/1)
+
+**Fixed bugs:**
+
+- Exception when removing a non existing key on iOS [\#4](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/4)
+
+**Closed issues:**
+
+- Can't find variable: SAMKeychain [\#3](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/3)
+- error while using lib with typescript [\#2](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/2)
+
+## [2.0.0](https://github.com/eddyverbruggen/nativescript-secure-storage/tree/2.0.0) (2016-11-28)
+
+
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
+
+
+## License
+
+Apache License Version 2.0
diff --git a/packages/secure-storage/common.ts b/packages/secure-storage/common.ts
new file mode 100644
index 00000000..90b27956
--- /dev/null
+++ b/packages/secure-storage/common.ts
@@ -0,0 +1,83 @@
+import { ApplicationSettings } from '@nativescript/core';
+
+export interface SetOptions {
+ service?: string;
+ key: string;
+ value: string;
+}
+
+export interface GetOptions {
+ service?: string;
+ key: string;
+}
+
+export interface RemoveOptions {
+ service?: string;
+ key: string;
+}
+
+export interface RemoveAllOptions {
+ service?: string;
+}
+
+export abstract class SecureStorageCommon {
+ protected static IS_FIRST_RUN = '__IS_FIRST_RUN__';
+ private isFirst: boolean;
+
+ constructor() {
+ this.isFirst = ApplicationSettings.getBoolean(SecureStorageCommon.IS_FIRST_RUN, true);
+ if (this.isFirst) {
+ ApplicationSettings.setBoolean(SecureStorageCommon.IS_FIRST_RUN, false);
+ }
+ }
+
+ abstract get(arg: GetOptions): Promise;
+
+ abstract getSync(arg: GetOptions): any;
+
+ abstract set(arg: SetOptions): Promise;
+
+ abstract setSync(arg: SetOptions): boolean;
+
+ abstract remove(arg: RemoveOptions): Promise;
+
+ abstract removeSync(arg: RemoveOptions): boolean;
+
+ abstract removeAll(arg?: RemoveAllOptions): Promise;
+
+ abstract removeAllSync(arg?: RemoveAllOptions): boolean;
+
+ public isFirstRunSync(): boolean {
+ return this.isFirst;
+ }
+
+ public isFirstRun(): Promise {
+ return new Promise((resolve, reject) => {
+ resolve(this.isFirstRunSync());
+ });
+ }
+
+ public clearAllOnFirstRun(): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isFirstRunSync()) {
+ this.removeAll();
+ resolve(true);
+ } else {
+ resolve(false);
+ }
+ });
+ }
+
+ public clearAllOnFirstRunSync(): boolean {
+ try {
+ if (this.isFirstRunSync()) {
+ this.removeAllSync();
+ return true;
+ }
+ return false;
+ } catch (e) {
+ console.log(e);
+ return false;
+ }
+ }
+}
diff --git a/packages/secure-storage/index.android.ts b/packages/secure-storage/index.android.ts
new file mode 100644
index 00000000..df6e37f3
--- /dev/null
+++ b/packages/secure-storage/index.android.ts
@@ -0,0 +1,53 @@
+import { GetOptions, SetOptions, RemoveOptions, RemoveAllOptions, SecureStorageCommon } from './common';
+import { Utils } from '@nativescript/core';
+
+declare const com: any;
+
+export class SecureStorage extends SecureStorageCommon {
+ private hawk: any; // com.orhanobut.hawk.Hawk
+
+ constructor() {
+ super();
+ this.hawk = com.orhanobut.hawk.Hawk.init(Utils.android.getApplicationContext()).build();
+ }
+
+ public get(arg: GetOptions): Promise {
+ return new Promise((resolve, reject) => {
+ resolve(com.orhanobut.hawk.Hawk.get(arg.key));
+ });
+ }
+
+ getSync(arg: GetOptions): any {
+ return com.orhanobut.hawk.Hawk.get(arg.key);
+ }
+
+ public set(arg: SetOptions): Promise {
+ return new Promise((resolve, reject) => {
+ resolve(com.orhanobut.hawk.Hawk.put(arg.key, arg.value));
+ });
+ }
+
+ setSync(arg: SetOptions): boolean {
+ return com.orhanobut.hawk.Hawk.put(arg.key, arg.value);
+ }
+
+ public remove(arg: RemoveOptions): Promise {
+ return new Promise((resolve, reject) => {
+ resolve(com.orhanobut.hawk.Hawk.delete(arg.key));
+ });
+ }
+
+ removeSync(arg: RemoveOptions): boolean {
+ return com.orhanobut.hawk.Hawk.delete(arg.key);
+ }
+
+ public removeAll(arg?: RemoveAllOptions): Promise {
+ return new Promise((resolve, reject) => {
+ resolve(com.orhanobut.hawk.Hawk.deleteAll());
+ });
+ }
+
+ public removeAllSync(arg?: RemoveAllOptions): boolean {
+ return com.orhanobut.hawk.Hawk.deleteAll();
+ }
+}
diff --git a/packages/secure-storage/index.d.ts b/packages/secure-storage/index.d.ts
new file mode 100644
index 00000000..8496b013
--- /dev/null
+++ b/packages/secure-storage/index.d.ts
@@ -0,0 +1,8 @@
+/**
+ * iOS and Android apis should match.
+ * It doesn't matter if you export `.ios` or `.android`, either one but only one.
+ */
+export * from './index.ios';
+
+// Export any shared classes, constants, etc.
+export * from './common';
diff --git a/packages/secure-storage/index.ios.ts b/packages/secure-storage/index.ios.ts
new file mode 100644
index 00000000..9d3632af
--- /dev/null
+++ b/packages/secure-storage/index.ios.ts
@@ -0,0 +1,191 @@
+import { GetOptions, RemoveAllOptions, RemoveOptions, SecureStorageCommon, SetOptions } from './common';
+
+declare const SAMKeychainQuery, SAMKeychain;
+
+export class SecureStorage extends SecureStorageCommon {
+ private isSimulator: boolean;
+ private accessibilityType: string;
+
+ private static defaultService: string = 'my_app';
+
+ // This is a copy of 'kSSKeychainAccountKey_copy' which is not exposed from SSKeychain.h by {N}
+ private static kSSKeychainAccountKey_copy: string = 'acct';
+
+ constructor(accessibilityType: string = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, disableFallbackToUserDefaults = false) {
+ super();
+
+ if (disableFallbackToUserDefaults) {
+ this.isSimulator = false;
+ } else {
+ const isMinIOS9 = NSProcessInfo.processInfo.isOperatingSystemAtLeastVersion({
+ majorVersion: 9,
+ minorVersion: 0,
+ patchVersion: 0,
+ });
+ if (isMinIOS9) {
+ const simDeviceName = NSProcessInfo.processInfo.environment.objectForKey('SIMULATOR_DEVICE_NAME');
+ this.isSimulator = simDeviceName !== null;
+ } else {
+ this.isSimulator = UIDevice.currentDevice.name.toLowerCase().indexOf('simulator') > -1;
+ }
+ }
+
+ this.accessibilityType = accessibilityType;
+ }
+
+ public get(arg: GetOptions): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isSimulator) {
+ resolve(NSUserDefaults.standardUserDefaults.objectForKey(arg.key));
+ return;
+ }
+
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+
+ try {
+ query.fetch();
+ resolve(query.password);
+ } catch (e) {
+ resolve(null);
+ }
+ });
+ }
+
+ getSync(arg: GetOptions): any {
+ if (this.isSimulator) {
+ return NSUserDefaults.standardUserDefaults.objectForKey(arg.key);
+ }
+
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+ try {
+ query.fetch();
+ return query.password;
+ } catch (e) {
+ return null;
+ }
+ }
+
+ public set(arg: SetOptions): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isSimulator) {
+ NSUserDefaults.standardUserDefaults.setObjectForKey(arg.value, arg.key);
+ resolve(true);
+ return;
+ }
+
+ SAMKeychain.setAccessibilityType(this.accessibilityType);
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+ query.password = arg.value;
+ resolve(query.save());
+ });
+ }
+
+ setSync(arg: SetOptions): boolean {
+ if (this.isSimulator) {
+ NSUserDefaults.standardUserDefaults.setObjectForKey(arg.value, arg.key);
+ return true;
+ }
+
+ SAMKeychain.setAccessibilityType(this.accessibilityType);
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+ query.password = arg.value;
+ return query.save();
+ }
+
+ public remove(arg: RemoveOptions): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isSimulator) {
+ NSUserDefaults.standardUserDefaults.removeObjectForKey(arg.key);
+ resolve(true);
+ return;
+ }
+
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+ try {
+ resolve(query.deleteItem());
+ } catch (e) {
+ resolve(false);
+ }
+ });
+ }
+
+ removeSync(arg: RemoveOptions): boolean {
+ if (this.isSimulator) {
+ NSUserDefaults.standardUserDefaults.removeObjectForKey(arg.key);
+ return true;
+ }
+
+ let query = SAMKeychainQuery.new();
+ query.service = arg.service || SecureStorage.defaultService;
+ query.account = arg.key;
+ try {
+ return query.deleteItem();
+ } catch (e) {
+ return false;
+ }
+ }
+
+ public removeAll(arg?: RemoveAllOptions): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isSimulator) {
+ let defaults = NSUserDefaults.standardUserDefaults;
+ let bundleId = NSBundle.mainBundle.bundleIdentifier;
+ defaults.removePersistentDomainForName(bundleId);
+ resolve(true);
+ return;
+ }
+
+ const allAccounts = SAMKeychain.allAccounts();
+ if (allAccounts) {
+ for (let i = 0; i < allAccounts.count; i++) {
+ let key = allAccounts[i].objectForKey(SecureStorage.kSSKeychainAccountKey_copy);
+ try {
+ let query = SAMKeychainQuery.new();
+ query.service = arg && arg.service ? arg.service : SecureStorage.defaultService;
+ query.account = key;
+ query.deleteItem();
+ } catch (e) {
+ console.log('SecureStorage: Could not remove key -> ' + key);
+ }
+ }
+ }
+
+ resolve(true);
+ });
+ }
+
+ public removeAllSync(arg?: RemoveAllOptions): boolean {
+ if (this.isSimulator) {
+ let defaults = NSUserDefaults.standardUserDefaults;
+ let bundleId = NSBundle.mainBundle.bundleIdentifier;
+ defaults.removePersistentDomainForName(bundleId);
+ return true;
+ }
+
+ const allAccounts = SAMKeychain.allAccounts();
+ if (allAccounts) {
+ for (let i = 0; i < allAccounts.count; i++) {
+ let key = allAccounts[i].objectForKey(SecureStorage.kSSKeychainAccountKey_copy);
+ try {
+ let query = SAMKeychainQuery.new();
+ query.service = arg && arg.service ? arg.service : SecureStorage.defaultService;
+ query.account = key;
+ query.deleteItem();
+ } catch (e) {
+ console.log('SecureStorage: Could not remove key -> ' + key);
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/packages/secure-storage/package.json b/packages/secure-storage/package.json
new file mode 100644
index 00000000..af77bed0
--- /dev/null
+++ b/packages/secure-storage/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@nativescript/secure-storage",
+ "version": "3.0.0",
+ "description": "Secure Storage NativeScript plugin",
+ "main": "secure-storage",
+ "typings": "index.d.ts",
+ "nativescript": {
+ "platforms": {
+ "android": "7.0.0",
+ "ios": "7.0.0"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/NativeScript/plugins.git"
+ },
+ "keywords": [
+ "NativeScript",
+ "JavaScript",
+ "TypeScript",
+ "iOS",
+ "Android"
+ ],
+ "author": {
+ "name": "NativeScript",
+ "email": "oss@nativescript.org"
+ },
+ "contributors": [
+ {
+ "name": "Eddy Verbruggen",
+ "email": "eddyverbruggen@gmail.com"
+ }
+ ],
+ "bugs": {
+ "url": "https://github.com/NativeScript/plugins/issues"
+ },
+ "license": "MIT",
+ "homepage": "https://github.com/NativeScript/plugins",
+ "readmeFilename": "README.md",
+ "bootstrapper": "@nativescript/plugin-seed"
+}
diff --git a/packages/secure-storage/platforms/android/include.gradle b/packages/secure-storage/platforms/android/include.gradle
new file mode 100644
index 00000000..55114212
--- /dev/null
+++ b/packages/secure-storage/platforms/android/include.gradle
@@ -0,0 +1,7 @@
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'com.orhanobut:hawk:2.0.1'
+}
\ No newline at end of file
diff --git a/packages/secure-storage/platforms/ios/Podfile b/packages/secure-storage/platforms/ios/Podfile
new file mode 100644
index 00000000..0e15a688
--- /dev/null
+++ b/packages/secure-storage/platforms/ios/Podfile
@@ -0,0 +1 @@
+pod 'SAMKeychain', '~> 1.5.3'
\ No newline at end of file
diff --git a/packages/secure-storage/project.json b/packages/secure-storage/project.json
new file mode 100644
index 00000000..e1237094
--- /dev/null
+++ b/packages/secure-storage/project.json
@@ -0,0 +1,64 @@
+{
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "sourceRoot": "packages/secure-storage",
+ "targets": {
+ "build": {
+ "executor": "@nrwl/js:tsc",
+ "options": {
+ "outputPath": "dist/packages/secure-storage",
+ "tsConfig": "packages/secure-storage/tsconfig.json",
+ "packageJson": "packages/secure-storage/package.json",
+ "main": "packages/secure-storage/index.d.ts",
+ "assets": [
+ "packages/secure-storage/*.md",
+ "packages/secure-storage/index.d.ts",
+ "LICENSE",
+ {
+ "glob": "**/*",
+ "input": "packages/secure-storage/platforms/",
+ "output": "./platforms/"
+ }
+ ],
+ "dependsOn": [
+ {
+ "target": "build.all",
+ "projects": "dependencies"
+ }
+ ]
+ }
+ },
+ "build.all": {
+ "executor": "@nrwl/workspace:run-commands",
+ "options": {
+ "commands": ["node tools/scripts/build-finish.ts secure-storage"],
+ "parallel": false
+ },
+ "outputs": ["dist/packages/secure-storage"],
+ "dependsOn": [
+ {
+ "target": "build.all",
+ "projects": "dependencies"
+ },
+ {
+ "target": "build",
+ "projects": "self"
+ }
+ ]
+ },
+ "focus": {
+ "executor": "@nrwl/workspace:run-commands",
+ "options": {
+ "commands": ["nx g @nativescript/plugin-tools:focus-packages secure-storage"],
+ "parallel": false
+ }
+ },
+ "lint": {
+ "executor": "@nrwl/linter:eslint",
+ "options": {
+ "lintFilePatterns": ["packages/secure-storage/**/*.ts"]
+ }
+ }
+ },
+ "tags": []
+}
diff --git a/packages/secure-storage/references.d.ts b/packages/secure-storage/references.d.ts
new file mode 100644
index 00000000..22bac92c
--- /dev/null
+++ b/packages/secure-storage/references.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/secure-storage/tsconfig.json b/packages/secure-storage/tsconfig.json
new file mode 100644
index 00000000..52cfe522
--- /dev/null
+++ b/packages/secure-storage/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "rootDir": "."
+ },
+ "exclude": ["**/*.spec.ts", "**/*.test.ts", "angular"],
+ "include": ["**/*.ts", "references.d.ts"]
+}
diff --git a/tools/demo/index.ts b/tools/demo/index.ts
index 692a61fb..0cd06e48 100644
--- a/tools/demo/index.ts
+++ b/tools/demo/index.ts
@@ -29,6 +29,7 @@ export * from './local-notifications';
export * from './localize';
export * from './pdf';
export * from './picker';
+export * from './secure-storage';
export * from './shared-notification-delegate';
export * from './social-share';
export * from './swift-ui';
diff --git a/tools/demo/secure-storage/index.ts b/tools/demo/secure-storage/index.ts
new file mode 100644
index 00000000..b03aba3d
--- /dev/null
+++ b/tools/demo/secure-storage/index.ts
@@ -0,0 +1,102 @@
+import { DemoSharedBase } from '../utils';
+import { SecureStorage } from '@nativescript/secure-storage';
+
+export class DemoSharedSecureStorage extends DemoSharedBase {
+ lastRetrievedValue = '';
+ secureStorage = new SecureStorage();
+
+ constructor() {
+ super();
+ }
+
+ testIt() {
+ console.log('test secure-storage!');
+ }
+
+ doSetSync() {
+ const success = this.secureStorage.setSync({
+ key: 'foo',
+ value: 'I was set at ' + new Date(),
+ });
+ console.log('Successfully set a value? ' + success);
+ }
+
+ doSet() {
+ this.secureStorage
+ .set({
+ key: 'foo',
+ value: 'I was set at ' + new Date(),
+ })
+ .then(
+ (success) => {
+ console.log('Successfully set a value? ' + success);
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ doGet() {
+ this.secureStorage
+ .get({
+ key: 'foo',
+ })
+ .then(
+ (value) => {
+ console.log('Value: ' + value);
+ this.lastRetrievedValue = value === null ? '(no value set)' : value;
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ doGetSync() {
+ const value = this.secureStorage.getSync({
+ key: 'foo',
+ });
+ this.lastRetrievedValue = value === null ? '(no value set)' : value;
+ }
+
+ doRemove() {
+ this.secureStorage
+ .remove({
+ key: 'foo',
+ })
+ .then(
+ (success) => {
+ console.log('Successfully removed a value? ' + success);
+ this.lastRetrievedValue = '';
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ doRemoveSync() {
+ this.secureStorage.removeSync({
+ key: 'foo',
+ });
+ this.lastRetrievedValue = '';
+ }
+
+ doRemoveAll() {
+ this.secureStorage.removeAll().then(
+ (success) => {
+ console.log('Successfully removed all values? ' + success);
+ this.lastRetrievedValue = '';
+ },
+ (err) => {
+ console.log(err);
+ }
+ );
+ }
+
+ doRemoveAllSync() {
+ this.secureStorage.removeAllSync();
+ this.lastRetrievedValue = '';
+ }
+}
diff --git a/tools/workspace-scripts.js b/tools/workspace-scripts.js
index 68d3bbff..5aaa9906 100644
--- a/tools/workspace-scripts.js
+++ b/tools/workspace-scripts.js
@@ -313,6 +313,13 @@ module.exports = {
description: '@nativescript/jetpack-compose: Build',
},
},
+ // @nativescript/secure-storage
+ 'secure-storage': {
+ build: {
+ script: 'nx run secure-storage:build.all',
+ description: '@nativescript/secure-storage: Build',
+ },
+ },
'build-all': {
script: 'nx run-many --target=build.all --all',
description: 'Build all packages',
@@ -467,6 +474,10 @@ module.exports = {
script: 'nx run jetpack-compose:focus',
description: 'Focus on @nativescript/jetpack-compose',
},
+ 'secure-storage': {
+ script: 'nx run secure-storage:focus',
+ description: 'Focus on @nativescript/secure-storage',
+ },
reset: {
script: 'nx g @nativescript/plugin-tools:focus-packages',
description: 'Reset Focus',
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 904bdb23..1ec6ccb3 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -62,7 +62,8 @@
"@nativescript/swift-ui": ["packages/swift-ui/index.d.ts"],
"@nativescript/theme-switcher": ["packages/theme-switcher/index.ts"],
"@nativescript/twitter": ["packages/twitter/index.d.ts"],
- "@nativescript/zip": ["packages/zip/index.d.ts"]
+ "@nativescript/zip": ["packages/zip/index.d.ts"],
+ "@nativescript/secure-storage": ["packages/secure-storage/index.d.ts"]
}
},
"exclude": ["node_modules", "tmp"]