Helps you define secure storages for your properties using Swift property wrappers.

Last update: Jun 23, 2022

๐Ÿ” Secure Property Storage

Helps you define secure storages for your properties using Swift property wrappers.

Twitter Swift License Swift Package Manager Carthage Bitrise Quality Maintainability Coverage Documentation

๐ŸŒŸ Features

All keys are hashed using SHA512 and all values are encrypted using AES-GCM to keep user information safe, automagically. Symmetric key is stored in Keychain in a totally secure way.

๐Ÿ’ Basic usage

@UserDefault

This property wrapper will store your property in UserDefaults using StoreKey (any String but i recommend you a String typed enum). Optionally, you can assign a default value to the property that will be secure stored at initialization.

@UserDefault(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

UserDefaultsStorage is also available, a subclass of UserDefaults with all the security provided by this library, where you can customize suite name.

@Keychain

This property wrapper will store your property in Keychain using StoreKey.

@Keychain(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As UserDefaultsStorage, KeychainStorage is also available, where you can customize access, group and synchronize it with iCloud.

@Singleton

This property wrapper will store your property in a memory singleton, every property with the same wrapper and key can access or modify the value from wherever it is.

@Singleton(<#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As KeychainStorage, SingletonStorage is also available.

@Inject

This property wrapper is similar to @Singleton but, together with @Register, will inject your dependencies. More details in Dependency Injection usage guide.

@Inject
var yourDependency: YourProtocol?

As SingletonStorage, InjectStorage is also available.

@Store

This is a custom wrapper, you can define your own Storage protocol implementation.

@Store(<#YourStorage#>, <#StoreKey#>)
var yourProperty: YourType? = yourDefaultValueIfNeeded

As InjectStorage, DelegatedStorage is also available with all the magic of this library.

๐Ÿง™โ€โ™‚๏ธ Codable usage

If your property conforms Codable protocol, just add Codable keyword as prefix of your property wrapper.

  • @CodableUserDefault
  • @CodableKeychain
  • @CodableSingleton
  • @CodableStore

๐Ÿฅก Unwrapped usage

To avoid continually unwrapping your property, just add Unwrapped keyword as prefix of your property wrapper, assign a default value (mandatory except for @UnwrappedInject), and it will return stored value or default value, but your property will always be there for you.

  • @UnwrappedUserDefault
  • @UnwrappedKeychain
  • @UnwrappedSingleton
  • @UnwrappedInject
  • @UnwrappedStore

๐Ÿฅก + ๐Ÿง™โ€โ™‚๏ธ Combo usage

You can also combine previous cases in case you need it, unwrapped first please.

  • @UnwrappedCodableUserDefault
  • @UnwrappedCodableKeychain
  • @UnwrappedCodableSingleton
  • @UnwrappedCodableStore

๐Ÿ’‰ Dependency Injection usage

@Register (click to expand)

This property wrapper will register the implementations of your dependencies. Register them wherever you want before inject it, but be sure to do it only once (except if you use qualifiers), for example, in an Injector class. You can register through a protocol or directly using your class implementation.

@Register
var yourDependency: YourProtocol = YourImplementation()

@Register
var yourDependency = YourImplementation()

You can also define a closure that builds your dependency. Just remember cast your dependency if you are going to inject it through a protocol.

@Register
var yourDependency = {
    YourImplementation() as YourProtocol
}

@Register
var yourDependency = {
    YourImplementation()
}
@Inject and @UnwrappedInject (click to expand)

These property wrappers injects your dependencies @Register implementations.

@Inject
var yourDependency: YourProtocol?

@Inject
var yourDependency: YourImplementation?

@UnwrappedInject
var yourUnwrappedDependency: YourProtocol

@UnwrappedInject
var yourUnwrappedDependency: YourImplementation

Scope

Because these property wrappers works similarly to @Singleton, the default scope is .singleton, but if you use builder closures on @Register, you can modify them to inject a single instance.

@Inject(.instance)
var yourDependency: YourProtocol?

@UnwrappedInject(.instance)
var yourUnwrappedDependency: YourProtocol
@InjectWith and @UnwrappedInjectWith (click to expand)

Your dependency may need parameters when injecting, you can pass them with these property wrappers. Simply define a model with your dependency parameters and pass it. It will inject a new instance built with these parameters.

@Register
var yourDependency = { parameters in
    YourImplementation(parameters) as YourProtocol
}

@Inject(YourParameters())
var yourDependency: YourProtocol?

@UnwrappedInject(YourParameters())
var yourUnwrappedDependency: YourProtocol
Qualifiers (click to expand)

You can use qualifiers to provide various implementations of a particular dependency. A qualifier is just a @objc protocol that you apply to a class.

For example, you could declare Dog and Cat qualifier protocols and apply it to another class that conforms Animal protocol. To declare this qualifier, use the following code:

protocol Animal {
  func sound()
}

@objc protocol Dog {}

@objc protocol Cat {}

You can then define multiple classes that conforms Animal protocol and uses this qualifiers:

class DogImplementation: Animal, Dog {
    func sound() { print("Woof!") }
}

class CatImplementation: Animal, Cat {
    func sound() { print("Meow!") }
}

Both implementations of the class can now be @Register:

@Register
var registerDog: Animal = DogImplementation()

@Register
var registerCat: Animal = CatImplementation()

To inject one or the other implementation, simply add the qualifier(s) to your @Inject:

@UnwrappedInject(Dog.self)
var dog: Animal

@UnwrappedInject(Cat.self)
var cat: Animal

dog.sound() // prints Woof!
cat.sound() // prints Meow!
Testing (click to expand)

One of the advantages of dependency injection is that the code can be easily testable with mock implementation. That is why there is a Mock qualifier that has priority over all, so you can have your dependencies defined in the app and create your mock in the test target simply by adding this qualifier.

// App target

class YourImplementation: YourProtocol {}

@Register
var yourDependency: YourProtocol = YourImplementation()

@Inject
var yourDependency: YourProtocol?
// Test target

class YourMock: YourProtocol, Mock {}

@Register
var yourDependency: YourProtocol = YourMock()

๐Ÿ‘€ Examples

Talk is cheap. Show me the code.

    // Securely stored in UserDefaults.
    @UserDefault("username")
    var username: String?

    // Securely stored in Keychain.
    @Keychain("password")
    var password: String?

    // Securely stored in a Singleton storage.
    @Singleton("sessionToken")
    var sessionToken: String?

    // Securely stored in a Singleton storage.
    // Always has a value, the stored or the default.
    @UnwrappedSingleton("refreshToken")
    var refreshToken: String = "B0610306-A33F"

    struct User: Codable {
        let username: String
        let password: String?
        let sessionToken: String?
    }

    // Codable model securely stored in UserDefaults.
    @CodableUserDefault("user")
    var user: User?

๐Ÿ›  Compatibility

  • macOS 10.15+
  • iOS 13.0+
  • iPadOS 13.0+
  • tvOS 13.0+
  • watchOS 6.0+

โš™๏ธ Installation

You can use the Swift Package Manager by declaring SecurePropertyStorage as a dependency in your Package.swift file:

.package(url: "https://github.com/alexruperez/SecurePropertyStorage", from: "0.3.0")

By default, all property wrappers are installed and you can import them, but if you want, you can install only some of them:

  • UserDefault: @*UserDefault property wrappers.
  • Keychain: @*Keychain property wrappers.
  • Singleton: @*Singleton property wrappers.
  • Storage: @*Store property wrappers.
  • Inject: @*Inject property wrappers.

For more information, see the Swift Package Manager documentation.

Or you can use Carthage:

github "alexruperez/SecurePropertyStorage"

๐Ÿป Etc.

  • Featured in Dave Verwer's iOS Dev Weekly - Issue 450, thanks Dave!
  • Contributions are very welcome. Thanks Alberto Garcia and Chen!
  • Attribution is appreciated (let's spread the word!), but not mandatory.

๐Ÿ‘จโ€๐Ÿ’ป Author

Alex Rupรฉrez โ€“ @alexruperez โ€“ [email protected]

๐Ÿ‘ฎโ€โ™‚๏ธ License

SecurePropertyStorage is available under the MIT license. See the LICENSE file for more info.

GitHub

https://github.com/alexruperez/SecurePropertyStorage
Comments
  • 1. Add targets to Carthage

    Carthage was only building Inject module. With this PR it'll build the following targets:

    • Inject
    • Keychain
    • Singleton
    • Storage
    • UserDefault

    This change isโ€‚Reviewable

    Reviewed by AlbGarciam at 2021-05-07 16:08
  • 2. Fix #15: `Unwrapped` always return defaultValue after restart app

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update. [x] Bug fix. [ ] New feature. [ ] Other, please explain:

    What changes did you make? (Give an overview) Fix bug #15 Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on?


    This change isโ€‚Reviewable

    Reviewed by qchenqizhi at 2021-04-19 08:49
  • 3. Renaming UserDefault property wrapper

    What is the purpose of this pull request? (put an "X" next to item)

    • [ ] Documentation update.
    • [ ] Bug fix.
    • [ ] New feature.
    • [x] Other, please explain:

    What changes did you make? (Give an overview) Renamed @UserDefault (and related) property wrappers to @SecureUserDefault because the type name as well as the target name conflicts with other Swift Packages (i.e. Guillermo Muntaner's Burritos) that also provide UserDefault-related property wrappers (although with different functionality).

    Which issue (if any) does this pull request address? This does not address a specific existing issue, however it does resolve SPM import conflicts on Xcode 11+.

    Is there anything you'd like reviewers to focus on? I realize that this PR may be invalid, or moving in an undesirable direction for the project. However, I thought it was worthwhile to share not only as a fix for the aforementioned issue, but because a more verbose name makes it clearer what the function of this property wrapper is. In fact, I believe that the readability of code using this library would be greatly improved if all of the included property wrappers were prepended with Secure. Of course, that might just be a picky programmer preference I have -- to each their own!

    Regardless, I love the package and am very thankful for your hard work and contribution to the community. I'll be using it in more than a few App Store projects :) ๐Ÿฅ‚


    This change isโ€‚Reviewable

    Reviewed by Sam-Spencer at 2020-08-19 17:14
  • 4. Support for Cocoapods?

    I'd love to use SPM but we have other dependencies that aren't available there yet, and using multiple managers in the same project isn't a road I'd like to go down. Can you release there as well?

    Reviewed by Vortec4800 at 2020-06-14 00:37
  • 5. `Unwrapped` always return defaultValue after restart app

    Describe the bug Unwrapped always return defaultValue after restart app

    To Reproduce Steps to reproduce the behavior:

        @UnwrappedCodableUserDefault(wrappedValue: "123", "key")
        var string: String
    

    set the value for var string to "234", then restart app, it's value become to "123"

    Expected behavior defaultValue should only returned when never set the value.

    Environment

    Additional context

    Reviewed by qchenqizhi at 2021-04-19 08:47
  • 6. Nonce reuse is bad

    For some unknown reason this library is using a fixed nonce. This is bad as AES-GCM can be trivially broken if a nonce is ever reused with the same key ( https://www.elttam.com/blog/key-recovery-attacks-on-gcm/ ).

    I suggest the following: Remove all the nonce generation and saving logic in the keychain and all the custom nonce management code. AES.GCM.seal() will generate a random nonce if none is provided. The nonce does not need to be saved/generated/handled: AES.GCM.seal will serialize the nonce with the cyphertext in the combined representation and automatically read it back in constructor : AES.GCM.SealedBox(combined: data). It is totally transparant to the user ot the AES.GCM api.

    Note: a key can be reused securely up to a certain extent with AES-GCM (IIRC 64Gb max per key), thus to have a really secure storage, a key rotation should be implemented.

    Reviewed by shphilippe at 2021-04-14 23:34
  • 7. Xcode 12 update.

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update. [x] Bug fix. [ ] New feature. [ ] Other, please explain:

    What changes did you make? (Give an overview)

    Xcode 12 update.

    Which issue (if any) does this pull request address?

    Unit tests fix.


    This change isโ€‚Reviewable

    Reviewed by alexruperez at 2020-09-29 09:43
  • 8. Allow register dependencies on demand.

    Is your feature request related to a problem? Please describe. SecurePropertyStorage is designed to register all dependencies at once, in application(_:didFinishLaunchingWithOptions:) for example.

    Describe the solution you'd like Register dependencies them on demand.

    Requested by @fnicola1

    Reviewed by alexruperez at 2021-11-16 21:48
  • 9. Added Dependency Injection Groups and Set support.

    What is the purpose of this pull request?

    [x] New feature. [x] Documentation update.

    What changes did you make?

    • Dependency Injection Groups.
    • Set support.

    This change isโ€‚Reviewable

    Reviewed by alexruperez at 2021-08-03 21:08
  • 10. #19 Add targets to Carthage

    #19 Add targets to Carthage by @AlbGarciam

    Also updated Swift Tools to 5.3 and mentioning @AlbGarciam and @qchenqizhi as contributors in the README.md ๐Ÿ˜ƒ


    This change isโ€‚Reviewable

    Reviewed by alexruperez at 2021-05-08 09:20
  • 11. When building a binary stable framework, keychain wrapper seems to not be located

    Describe the bug When providing a binary stable framework, .swiftinterface file raises the following error:

    image

    To Reproduce Steps to reproduce the behavior:

    1. Add SecurePropertyStorageDynamic as xcframework dependency
    2. Generate the stable xcframework (disabling SKIP_INSTALL and enabling BUILD_LIBRARY_FOR_DISTRIBUTION)
    3. Integrate into a separate app/framework, it will raise the issue displayed on the screenshot

    Expected behavior Property wrapper can be used without any other issue

    Environment

    • Version 0.4.1
    • Xcode: 12.4 to build the xcframework, 12.5 to build the app
    • Swift: 5.3

    Additional context When building the xcframework it raises the following warning: image

    And @_implementationOnly cannot be used as it won't allow to use the property wrapper due to the following error: image

    Reviewed by AlbGarciam at 2021-05-03 10:45
Cybr/Secure - A simple but powerful secure password generator
Cybr/Secure - A simple but powerful secure password generator

A simple but powerful secure password generator. You get the option of password length (10 to 20 characters) and whether you include numbers, symbols, uppercase and/or lowercase letters. Simply tap the lock icon to generate a secure password and then tap to copy the password.

Feb 16, 2022
CCCryptor (AES encryption) wrappers for iOS and Mac in Swift. -- For ObjC, see RNCryptor/RNCryptor-objc

RNCryptor Cross-language AES Encryptor/Decryptor data format. The primary targets are Swift and Objective-C, but implementations are available in C, C

Jun 25, 2022
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift

CryptoSwift Crypto related functions and helpers for Swift implemented in Swift. (#PureSwift) Note: The master branch follows the latest currently rel

Jun 20, 2022
Simple and secure hashing in Swift with the SipHash algorithm

SipHash โš ๏ธ WARNING This package has been obsoleted by the Hasher type and the Hashable.hash(into:) requirement introduced in Swift 4.2. Using this pac

Apr 13, 2022
The minimalistic, secure and open-source two-factor authentication app.
The minimalistic, secure and open-source two-factor authentication app.

Einmal /หˆainmaหl/ German: once The minimalistic, secure and open-source two-factor authentication app. Features โ™ป๏ธ Cross-platform โ€” available on Andro

Jun 7, 2022
PassDrop is a fully-featured secure password management system, compatible with the free KeePass 1.x (Classic) and multi-platform KeePassX desktop applications.

passdrop This is a modern, updated build of Rudis Muiznieks's PassDrop application. PassDrop is a fully-featured secure password management system, co

Feb 23, 2022
Simple, secure password and data management for individuals and teams

Padloc Simple, secure password and data management for individuals and teams (formerly known as Padlock). This repo is split into multiple packages: P

Jun 20, 2022
PGPro can encrypt and decrypt messages as well as manage all your OpenPGP keys. It is free, simple and lightweight. Everything stays on your device. PGPro is made in Switzerland.
PGPro can encrypt and decrypt messages as well as manage all your OpenPGP keys. It is free, simple and lightweight. Everything stays on your device. PGPro is made in Switzerland.

PGPro can encrypt and decrypt messages as well as manage all your OpenPGP keys. It is free, simple and lightweight. Everything stays on your device. P

Jun 24, 2022
Use Apple FaceID or TouchID authentication in your app using BiometricAuthentication.
Use Apple FaceID or TouchID authentication in your app using BiometricAuthentication.

BiometricAuthentication Use Apple FaceID or TouchID authentication in your app using BiometricAuthentication. It's very simple and easy to use that ha

Jun 15, 2022
A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift.

SwiftKeychainWrapper A simple wrapper for the iOS / tvOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift. Prov

Jun 25, 2022
Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works.

Valet Valet lets you securely store data in the iOS, tvOS, watchOS, or macOS Keychain without knowing a thing about how the Keychain works. Itโ€™s easy.

Jun 19, 2022
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

May 31, 2022
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

May 31, 2022
Obfuscate your strings in Swift easily

TPObfuscatedString TPObfuscatedString is a simple extension for String in Swift. It allows you to obfuscate hardcoded Strings in your compiled binary.

Jan 9, 2020
Swift cross-platform crypto library using CommonCrypto/libcrypto

BlueCryptor Swift cross-platform crypto library derived from IDZSwiftCommonCrypto. IMPORTANT NOTE: This release is NOT entirely source code compatible

May 18, 2022
Swift cross-platform crypto library using CommonCrypto/libcrypto

BlueCryptor Swift cross-platform crypto library derived from IDZSwiftCommonCrypto. IMPORTANT NOTE: This release is NOT entirely source code compatible

May 18, 2022
A tiny and easy to use Swift class to encrypt strings using HMAC algorithms.

#Sweet HMAC SweetHMAC is a tiny and easy to use Swift class to encrypt strings using HMAC algorithms. A special thanks to jernejstrasner for shared HM

Aug 15, 2021
a bunch of random programs using Swift

Random-SwiftStuff (WORK IN PROGRESS) a bunch of random programs using Swift: apps mini programs calculators password generators random number generato

Oct 16, 2021
The IDAGIO WatchOS app using swift
The IDAGIO WatchOS app using swift

IDAGIORedesignWatchOS I redesigned the IDAGIO WatchOS app as an exercise Old App

Dec 23, 2021