DEV Community

Anis Ali Khan
Anis Ali Khan

Posted on

Tutorial 31: ๐Ÿ“ฑ UserDefaults and Keychain - Storing Small Amounts of Data Securely

Build a Secure Notes iPhone App

๐Ÿ” In this tutorial, youโ€™ll learn how to securely store small amounts of data using UserDefaults and Keychain in Swift. Weโ€™ll build a Secure Notes app where users can create simple text notes, with the option to store them securely using Face ID / Touch ID authentication.

โธป

๐Ÿ“– Table of Contents

  1. ๐Ÿ“Œ Introduction
  2. ๐Ÿ›  Project Setup
  3. ๐Ÿ’พ Storing Data with UserDefaults
  4. ๐Ÿ” Storing Secure Data in Keychain
  5. ๐ŸŽจ Creating the UI
  6. ๐Ÿ“ฒ Implementing Face ID Authentication
  7. ๐Ÿš€ Running & Testing the App
  8. ๐Ÿ”— Conclusion & Next Steps

โธป

๐Ÿ“Œ Introduction

iOS provides two main ways to store small amounts of data securely:
โ€ข UserDefaults: Ideal for storing non-sensitive data like user preferences.
โ€ข Keychain: Used for storing sensitive data like passwords, API keys, or private notes.

In this tutorial, weโ€™ll build Secure Notes, a minimalist note-taking app where:
โœ… Normal notes are stored in UserDefaults*
โœ… Secure notes are stored in **Keychain
, requiring Face ID / Touch ID to access

โธป

๐Ÿ›  Project Setup

1๏ธโƒฃ Create a New Xcode Project

  1. Open Xcode and select โ€œCreate a new Xcode projectโ€
  2. Choose App, then click Next
  3. Set the following: โ€ข Product Name: SecureNotes โ€ข Interface: SwiftUI โ€ข Language: Swift
  4. Click Next, choose a location, and click Create.

2๏ธโƒฃ Enable Face ID (for later)

  1. In Xcode, go to Signing & Capabilities
  2. Click + Capability โ†’ Keychain Sharing
  3. Click + Capability โ†’ Face ID

โธป

๐Ÿ’พ Storing Data with UserDefaults

Weโ€™ll first store simple notes using UserDefaults.

1๏ธโƒฃ Create a Model for Notes

Create a new Swift file: Note.swift

import Foundation

struct Note: Identifiable, Codable {
    var id: UUID = UUID()
    var text: String
    var isSecure: Bool
}
Enter fullscreen mode Exit fullscreen mode

2๏ธโƒฃ Create a UserDefaults Helper

Create a new Swift file: UserDefaultsManager.swift

import Foundation

class UserDefaultsManager {
    private let key = "savedNotes"

    func saveNotes(_ notes: [Note]) {
        if let encoded = try? JSONEncoder().encode(notes) {
            UserDefaults.standard.set(encoded, forKey: key)
        }
    }

    func loadNotes() -> [Note] {
        if let savedData = UserDefaults.standard.data(forKey: key),
           let decoded = try? JSONDecoder().decode([Note].self, from: savedData) {
            return decoded
        }
        return []
    }
}
Enter fullscreen mode Exit fullscreen mode

โธป

๐Ÿ” Storing Secure Data in Keychain

Since Keychain doesnโ€™t support Swiftโ€™s Codable directly, weโ€™ll write a wrapper.

1๏ธโƒฃ Create a Keychain Helper

Create a new Swift file: KeychainManager.swift

import Security
import Foundation

class KeychainManager {
    static let shared = KeychainManager()

    func save(_ note: Note) {
        let key = note.id.uuidString
        guard let data = try? JSONEncoder().encode(note) else { return }

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: data
        ]
        SecItemAdd(query as CFDictionary, nil)
    }

    func load(id: UUID) -> Note? {
        let key = id.uuidString
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]

        var dataTypeRef: AnyObject?
        if SecItemCopyMatching(query as CFDictionary, &dataTypeRef) == errSecSuccess,
           let data = dataTypeRef as? Data,
           let note = try? JSONDecoder().decode(Note.self, from: data) {
            return note
        }
        return nil
    }
}
Enter fullscreen mode Exit fullscreen mode

โธป

๐ŸŽจ Creating the UI

Create a simple SwiftUI interface.

1๏ธโƒฃ Create the Notes List

Modify ContentView.swift

import SwiftUI

struct ContentView: View {
    @State private var notes: [Note] = UserDefaultsManager().loadNotes()
    @State private var newText: String = ""

    var body: some View {
        NavigationView {
            VStack {
                TextField("Enter your note", text: $newText)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()

                Button("Save Note") {
                    let note = Note(text: newText, isSecure: false)
                    notes.append(note)
                    UserDefaultsManager().saveNotes(notes)
                    newText = ""
                }
                .buttonStyle(.borderedProminent)

                List(notes) { note in
                    Text(note.text)
                }
            }
            .navigationTitle("Secure Notes")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

โธป

๐Ÿ“ฒ Implementing Face ID Authentication

  1. Add LocalAuthentication to ContentView.swift
import LocalAuthentication

func authenticateUser(completion: @escaping (Bool) -> Void) {
    let context = LAContext()
    var error: NSError?

    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Access Secure Notes") { success, _ in
            DispatchQueue.main.async {
                completion(success)
            }
        }
    } else {
        completion(false)
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Use Face ID before loading secure notes:
Button("Load Secure Note") {
    authenticateUser { success in
        if success {
            if let secureNote = KeychainManager.shared.load(id: someUUID) {
                print("Secure Note:", secureNote.text)
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

โธป

๐Ÿš€ Running & Testing the App

โœ… Run on a real device to test Face ID / Touch ID.
โœ… Save regular notes โ†’ Restart the app โ†’ Check if they persist.
โœ… Save a secure note โ†’ Restart โ†’ Authenticate with Face ID โ†’ Retrieve it.

โธป

๐Ÿ”— Conclusion & Next Steps

๐ŸŽ‰ Congratulations! Youโ€™ve built Secure Notes, learning:
โœ… How to use UserDefaults for non-sensitive data
โœ… How to use Keychain for secure storage
โœ… How to authenticate users with Face ID / Touch ID

๐Ÿ“Œ Next Steps:
โ€ข Add a delete note feature.
โ€ข Support multiple secure notes.
โ€ข Implement Dark Mode UI improvements.

๐Ÿš€ Happy Coding! ๐Ÿš€

Top comments (0)