---
title: Customize the storage client
description: The Ping (ForgeRock) Authenticator module lets you customize the storage client and manages all data through that client.
component: sdks
version: latest
page_id: sdks:authenticator-module:getting-started/03-customize-storage-clients
canonical_url: https://docs.pingidentity.com/sdks/latest/authenticator-module/getting-started/03-customize-storage-clients.html
revdate: Wed, 17 May 2023 14:10:20 +0100
keywords: ["Setup &amp; Configuration", "Integration"]
section_ids:
  android: Android
  ios: iOS
---

# Customize the storage client

The Ping (ForgeRock) Authenticator module lets you customize the storage client and manages all data through that client.

## Android

The Ping (ForgeRock) Authenticator module offers a default storage client that uses `SecuredSharedPreferences`, an encrypted storage mechanism built on Android [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences). It is available in the `forgerock-core` module.

`SecuredSharedPreferences` stores and manages all shared secret account information and notifications.

The Authenticator module lets you customize the `StorageClient`. You can implement the `StorageClient` interface, and register your own `StorageClient` in the module.

|   |                                                                                                                                                                                                                                                                                                                                                                                                 |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | For maximum compatibility with devices from different manufacturers we highly recommend that you implement your own custom storage client.The default client that is built on `SharedPreferences` can behave unpredictably on devices from certain manufacturers that customize the Android operating system.For example, you might not be able to access the registered PUSH or OATH accounts. |

You can implement your custom storage client in any location you choose, for example you could use the SQLite-based [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences).

The Ping (ForgeRock) Authenticator module uses your storage client and manages all data through that client.

To customize the `StorageClient`, implement the following interfaces:

```java
public interface StorageClient {
    /**
     * Get the Account object with its id
     * @param accountId The account unique ID
     * @return The account object.
     */
    Account getAccount(String accountId);
    /**
     * Get all accounts stored in the system.
     * @return The complete list of accounts.
     */
    List<Account> getAllAccounts();
    /**
     * Delete the Account that was passed in.
     * @param account The account object to delete.
     * @return boolean as result of the operation
     */
    boolean removeAccount(Account account);
    /**
     * Add or Update the Account to the storage system.
     * @param account The Account to store or update.
     * @return boolean as result of the operation
     */
    boolean setAccount(Account account);
    /**
     * Get the mechanisms associated with an account.
     * @param account The Account object
     * @return The list of mechanisms for the account.
     */
    List<Mechanism> getMechanismsForAccount(Account account);
    /**
     * Get the mechanism by UUID.
     * @param mechanismUID The uniquely identifiable UUID for the mechanism
     * @return The mechanism object.
     */
    Mechanism getMechanismByUUID(String mechanismUID);
    /**
     * Delete the mechanism uniquely identified by an id.
     * @param mechanism The mechanism object to delete.
     * @return boolean as result of the operation
     */
    boolean removeMechanism(Mechanism mechanism);
    /**
     * Add or update the mechanism to the storage system.
     * If the owning Account is not yet stored, store that as well.
     * @param mechanism The mechanism to store or update.
     * @return boolean as result of the operation
     */
    boolean setMechanism(Mechanism mechanism);
    /**
     * Get all notifications for within the mechanism.
     * @param mechanism The mechanism object
     * @return The list of notifications for the mechanism.
     */
    List<PushNotification> getAllNotificationsForMechanism(Mechanism mechanism);
    /**
     * Delete the pushNotification uniquely identified by an id.
     * @param pushNotification The pushNotification object to delete.
     */
    boolean removeNotification(PushNotification pushNotification);
    /**
     * Add or update the pushNotification to the storage system.
     * @param pushNotification The pushNotification to store.
     * @return boolean as result of the operation
     */
    boolean setNotification(PushNotification pushNotification);
    /**
     * Whether the storage system currently contains any data.
     * @return True if the storage system is empty, false otherwise.
     */
    boolean isEmpty();
}
```

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | For each method of getting an `Account`, `Mechanism`, or `PushNotification` object, your `StorageClient` should only be responsible for retrieving the objects, and not any other object associated with it.For example, when retrieving `Account` objects, the `StorageClient` should not be responsible for retrieving `Mechanism` and `PushNotification` objects. All object mapping and associations are handled by the Ping (ForgeRock) Authenticator module itself. |

After implementing your custom `StorageClient`, register it to `FRAClient` as follows:

```java
// Initiate your custom StorageClient
StorageClient customStorageClient = CustomStorageClient()

// Register it to FRAClient
FRAClient fraClient = new FRAClient.FRAClientBuilder()
                .withContext(this)
                .withStorage(customStorageClient)
                .start();
```

|   |                                                                                                                                                                                      |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | You must register the `StorageClient` **before** you start the Ping (ForgeRock) SDK.The `StorageClient` used by `FRAClient` cannot be changed after the Ping (ForgeRock) SDK starts. |

## iOS

The ForgeRock Authenticator default storage client utilizes both Apple's [Keychain Service](https://developer.apple.com/documentation/security/keychain_services), and [Secure Enclave](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave).

This means that the Ping (ForgeRock) Authenticator module safely stores all shared secrets, account information, and notifications.

You can also customize the `StorageClient`. You can implement the `StorageClient` protocol, and register your own `StorageClient` with the Ping (ForgeRock) Authenticator module.

For example, you could customize `StorageClient` to use SQLite, CoreData, or any other storage destination.

The Ping (ForgeRock) Authenticator module uses your storage client and manages all data through that client.

To customize `StorageClient` you must implement the following interfaces:

```swift
/// StorageClient protocol represents predefined interfaces and protocols for FRAuthenticator's storage method.
public protocol StorageClient {

    /// Stores Account object into Storage Client and returns discardable Boolean result of operation.
    /// - Parameter account: Account object to store.
    @discardableResult func setAccount(account: Account) -> Bool

    /// Removes Account object from Storage Client, and returns discardable Boolean result of operation.
    /// - Parameter account: Account object to remove.
    @discardableResult func removeAccount(account: Account) -> Bool

    /// Retrieves Account object with its unique identifier.
    /// - Parameter accountIdentifier: String value of Account's unique identifier.
    func getAccount(accountIdentifier: String) -> Account?

    /// Retrieves all Account objects stored in Storage Client.
    func getAllAccounts() -> [Account]

    /// Stores Mechanism object into Storage Client, and returns discardable Boolean result of operation.
    /// - Parameter mechanism: Mechanism object to store.
    @discardableResult func setMechanism(mechanism: Mechanism) -> Bool

    /// Removes Mechanism object from Storage Client, and returns discardable Boolean result of operation.
    /// - Parameter mechanism: Mechanism object to remove.
    @discardableResult func removeMechanism(mechanism: Mechanism) -> Bool

    /// Retrieves all Mechanism objects stored in Storage Client.
    /// - Parameter account: Account object that is associated with Mechanism(s).
    func getMechanismsForAccount(account: Account) -> [Mechanism]

    /// Retrieves Mechanism object with given Mechanism UUID.
    /// - Parameter uuid: UUID of Mechanism.
    func getMechanismForUUID(uuid: String) -> Mechanism?

    /// Stores PushNotification object into Storage Client, and returns discardable Boolean result of operation.
    /// - Parameter notification: PushNotification object to store.
    @discardableResult func setNotification(notification: PushNotification) -> Bool

    /// Removes PushNotification object from Storage Client, and returns discardable Boolean result of operation.
    /// - Parameter notification: PushNotification object to remove.
    @discardableResult func removeNotification(notification: PushNotification) -> Bool

    /// Retrieves all Notification objects from Storage Client with given Mechanism object.
    /// - Parameter mechanism: Mechanism object that is associated with Notification(s).
    func getAllNotificationsForMechanism(mechanism: Mechanism) -> [PushNotification]

    /// Returns whether or not StorageClient has any data stored.
    @discardableResult func isEmpty() -> Bool
}
```

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | For each method of getting an `Account`, `Mechanism`, or `PushNotification` object, your `StorageClient` should only be responsible for retrieving the objects, and not any other object associated with it.For example, when retrieving `Account` objects, the `StorageClient` should not be responsible for retrieving `Mechanism` and `PushNotification` objects. All object mapping and associations are handled by the Ping (ForgeRock) Authenticator module itself. |

After implementing your custom `StorageClient`, register it with your `FRAClient` as follows:

```swift
// Initiate your custom StorageClient
let customStorageClient = CustomStorageClient()
// Register it with your FRAClient
FRAClient.setStorage(storage: customStorageClient)
// Initiate the SDK
FRAClient.start()
```

|   |                                                                                                                                                                     |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | You must register the `StorageClient` **before** you start the Ping (ForgeRock) SDK.Once the SDK starts, the `StorageClient` used by `FRAClient` cannot be changed. |
