Swift. KeyChain. Property wrapper
Some time ago, an article was published on the topic of storing user data. This article presented a solution that is suitable for storing general data such as notification settings, mailing list, age, name, etc. But if you need to store security essential or private data, such as an authorization key, account password, account login, etc., then for these purposes you will need some kind of safe and secure storage. Next, we will talk about the Keychain storage and how to program it.
Let’s impress the necessary requirements for a secure storage.
- Ability to save data.
- Ability to load data.
- Possibility to delete data.
- Data can be both passwords or tokens, and accounts, i.e. login-password combinations.
The kernel for working with storage will be SecureStorage class, which satisfies the first 3 requirements.
Next, accounts. As stated in the documentation, storage is used to securely store small chunks of data. Therefore, accounts require some Credentials model, which will later be used.
Naturally, in order to use the Credentials model, a set of new methods will be required that take it as an argument.
Working with passwords is somewhat easier to program, since in this case only the password is stored, not the login-password combination, and String is used as the data type.
Thus, to use such a store, you can create the computed property with get and set methods, but it is much better to use @propertyWrapper instead of the computed property.
Pay attention to the nonmutating keyword in the set of each structure. Without this, when using the proposed property wrapper in SwiftUI, the compiler would throw an error “Cannot assign to property: ‘self’ is immutable”. Also, for the sake of compatibility with SwiftUI, an implementation of the DynamicProperty protocol was added.
As a result, the class containing the secure data looks as simple as possible, while implementing the requirements.
Moreover, you can copy the approach that is used in Apple — declare a shared instance (singleton), or pass an object to the environment (environmentObject), or create an instance by demand.
Storing authorization data is a fairly common task that developers solve, since the authorization token loses its relevance after a certain time. In order for the user experience to be as positive as possible, you need to re-authorize in a “silent” mode, and not navigate the user to the authorization screen.
In any case, this is one of the most common use cases for secure storage. Another example is the storage of the authToken-refreshToken combination. But more about that some other time.