Dead Simple Keychain Access

Login to Access Code

Keychain is one of the many cocoa libraries that has not seen a modern Swift-friendly implementation. We have set out to change that and provide to you a really simple wrapper around Keychain to simply get and set keychain objects with a key.

Add the Security Framework to your project

All you need to do is add Security library:

  1. Click on your main project folder
  2. Select Build Phases
  3. Add in Security.framework by clicking + under Link Binary with Libraries

Create a Unit Test

It’s always good to have Unit Tests. We’ve made a really basic one that you can use below. Just make sure that you add Security framework and Keychain.swift to your test target.

func testKeychainSet()
{
  let setKey = Keychain.set("foo", value: "test")
  let getKey = Keychain.get("foo") as! String

  XCTAssertTrue(setKey, "set keychain value with string")
  XCTAssertNotNil(getKey, "retrieve keychain value with key")
  XCTAssertEqual(getKey, "test", "retrieved keychain value matches raw string")
}

Keychain Source

The source is pretty straightforward and updated to Swift v1.2. You can set with Keychain.set("foo", value: "bar") and get the value with Keychain.get("foo") as well as completely delete an entry with Keychain.delete("foo").

import Security

public class Keychain
{
  public class func set(key: String, value: String) -> Bool
  {
    if let data = value.dataUsingEncoding(NSUTF8StringEncoding)
    {
      return set(key, value: data)
    }

    return false
  }

  public class func set(key: String, value: NSData) -> Bool
  {
    let query = [
      (kSecClass as! String)       : kSecClassGenericPassword,
      (kSecAttrAccount as! String) : key,
      (kSecValueData as! String)   : value
    ]

    SecItemDelete(query as CFDictionaryRef)

    return SecItemAdd(query as CFDictionaryRef, nil) == noErr
  }

  public class func get(key: String) -> NSString?
  {
    if let data = getData(key)
    {
      return NSString(data: data, encoding: NSUTF8StringEncoding)
    }

    return nil
  }

  public class func getData(key: String) -> NSData?
  {
    let query = [
      (kSecClass as! String)       : kSecClassGenericPassword,
      (kSecAttrAccount as! String) : key,
      (kSecReturnData as! String)  : kCFBooleanTrue,
      (kSecMatchLimit as! String)  : kSecMatchLimitOne
    ]

    var dataTypeRef: Unmanaged<AnyObject>?
    let status = SecItemCopyMatching(query, &dataTypeRef)

    if status == noErr && dataTypeRef != nil
    {
      return dataTypeRef!.takeRetainedValue() as? NSData
    }

    return nil
  }

  public class func delete(key: String) -> Bool
  {
    let query = [
      (kSecClass as! String)       : kSecClassGenericPassword,
      (kSecAttrAccount as! String) : key
    ]

    return SecItemDelete(query as CFDictionaryRef) == noErr
  }

  public class func clear() -> Bool
  {
    let query = [
      (kSecClass as String): kSecClassGenericPassword
    ]

    return SecItemDelete(query as CFDictionaryRef) == noErr
  }
}