Skip to content

Use the system Keychain in AppKit and UIKit as easily as AppStorage in SwiftUI — with seamless Codable support and optional value deletion baked in.

License

Notifications You must be signed in to change notification settings

tatewake/KeychainStorage

Repository files navigation

KeychainStorage

Use the system Keychain in AppKit and UIKit as easily as @AppStorage in SwiftUI — with seamless Codable support and optional value deletion baked in.

License

About

@KeychainStorage is a property wrapper that reflects a system Keychain value, allowing reactive updates via didSet when values change.

This is similar to @DefaultsStorage or @AppStorage for reading values from UserDefaults, but useful for reading and writing secure values to the Keychain.

Features

  • Simple property wrapper for storing and retrieving secure values. (Passwords, tokens, session IDs, etc.)
  • Supports all standard Codable types, including String, Int, etc, as well as user-defined, Codable conforming types.
  • Provides didSet-based reactivity.
  • Fully tested for reliability.

Installation

Swift Package Manager (SPM)

Add KeychainStorage to your project with Swift Package Manager in Xcode:

  1. Go to “File -> Add Package Dependency…”
  2. Enter the package URL: https://github.com/tatewake/KeychainStorage
  3. Use “Dependency Rule -> Exact Version” and set to use release version "1.0.0".

Usage

Simple Example

The @KeychainStorage tag will load from the Keychain on instantiation and save automatically when the values changes. If no value exists in the Keychain, the initializer will provide a default value.

@KeychainStorage("email") var userEmail = "[email protected]"

This syncs automatically on initialization or when modified in code.

Optional Values

Optional values allow you to delete data from the Keychain by setting it to nil:

@KeychainStorage("sessionID") var sessionID: String?

sessionID = "def456"   // Store new value into Keychain
sessionID = nil        // Delete item from Keychain

Custom Types

Any type conforming to Codable works out of the box:

struct User: Codable {
    let id: Int
    let name: String
}

@KeychainStorage("user") var user: User? = nil

The value is automatically encoded/decoded using JSONEncoder and JSONDecoder.

Reactive Example

Here’s an example using UserInfo as a struct to leverage didSet reactivity.

struct UserInfo {
    struct Login: Equatable {
        @DefaultsStorage("username") var username = ""
        @DefaultsStorage("password") var password = ""
    }

    struct Session: Equatable {
        @DefaultsStorage("token") var token: String?
        @DefaultsStorage("sessionID") var sessionID: String?
    }

    var login = Login()
    var session = Session()
}

Now, in a top-level Model class, we use didSet to handle changes in UserInfo via a delegate:

class Model {
	weak var delegate: MyDelegate?

	var userInfo = UserInfo() {
	    didSet {
	        if oldValue.login != settings.login {
	            delegate?.LoginChanged(login: settings.login)
	        }

	        if oldValue.session != settings.session {
	            delegate?.SessionChanged(session: settings.session)
	        }
	    }
	}
	...
}

This is just a simple example, but it shows the straightforward nature of KeychainStorage.

Caveats

Much like DefaultsStorage, there are two things to be aware of:

  1. External changes won’t update in real time, and
  2. Properties sharing the same key won’t stay in sync.

This is because @KeychainStorage only reads from the Keychain on initialization, and only internal code changes trigger the didSet handler.

Therefore, if another app or process updates your Keychain store, or two @KeychainStorage properties use the same key, these changes won't trigger reactively.

The first scenario is rare, and for the second, ensure a single variable per setting as the “source of truth.”

Acknowledgements

  • Inspired by SwiftUI’s @AppStorage.
  • Built as a companion to DefaultsStorage for handling secure data.

About

Use the system Keychain in AppKit and UIKit as easily as AppStorage in SwiftUI — with seamless Codable support and optional value deletion baked in.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages