Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 42 additions & 34 deletions SwiftKeychainWrapper/KeychainWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ private let SecAttrSynchronizable: String = kSecAttrSynchronizable as String

/// KeychainWrapper is a class to help make Keychain access in Swift more straightforward. It is designed to make accessing the Keychain services more like using NSUserDefaults, which is much more familiar to people.
open class KeychainWrapper {

public enum Error: Swift.Error {
case keychainStatus(OSStatus)
case noData
}

@available(*, deprecated, message: "KeychainWrapper.defaultKeychainWrapper is deprecated since version 2.2.1, use KeychainWrapper.standard instead")
public static let defaultKeychainWrapper = KeychainWrapper.standard
Expand Down Expand Up @@ -255,20 +260,20 @@ open class KeychainWrapper {

// MARK: Public Setters

@discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
try set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

@discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
try set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

@discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
try set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

@discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
try set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

/// Save a String value to the keychain associated with a specified key. If a String value already exists for the given key, the string will be overwritten with the new value.
Expand All @@ -278,11 +283,11 @@ open class KeychainWrapper {
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
if let data = value.data(using: .utf8) {
return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
try set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
} else {
return false
throw Error.noData
}
}

Expand All @@ -293,10 +298,10 @@ open class KeychainWrapper {
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
let data = NSKeyedArchiver.archivedData(withRootObject: value)

return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
try set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

/// Save a Data object to the keychain associated with a specified key. If data already exists for the given key, the data will be overwritten with the new value.
Expand All @@ -306,7 +311,7 @@ open class KeychainWrapper {
/// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
/// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
/// - returns: True if the save was successful, false otherwise.
@discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)

keychainQueryDictionary[SecValueData] = value
Expand All @@ -319,19 +324,22 @@ open class KeychainWrapper {
}

let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)

if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
return update(value, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
} else {
return false
return
}

if status == errSecDuplicateItem {
try update(value, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
return
}

throw Error.keychainStatus(status)
}

@available(*, deprecated, message: "remove is deprecated since version 2.2.1, use removeObject instead")
@discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
return removeObject(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
try removeObject(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
}

/// Remove an object associated with a specified key. If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with.
Expand All @@ -340,21 +348,21 @@ open class KeychainWrapper {
/// - parameter withAccessibility: Optional accessibility level to use when looking up the keychain item.
/// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
/// - returns: True if successful, false otherwise.
@discardableResult open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)

// Delete
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)

if status == errSecSuccess {
return true
} else {
return false
return
}

throw Error.keychainStatus(status)
}

/// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set.
@discardableResult open func removeAllKeys() -> Bool {
open func removeAllKeys() throws {
// Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]

Expand All @@ -369,10 +377,10 @@ open class KeychainWrapper {
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)

if status == errSecSuccess {
return true
} else {
return false
return
}

throw Error.keychainStatus(status)
}

/// Remove all keychain data, including data not added through keychain wrapper.
Expand Down Expand Up @@ -404,7 +412,7 @@ open class KeychainWrapper {
}

/// Update existing data associated with a specified key name. The existing data will be overwritten by the new data.
private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) throws {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
let updateDictionary = [SecValueData:value]

Expand All @@ -417,10 +425,10 @@ open class KeychainWrapper {
let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)

if status == errSecSuccess {
return true
} else {
return false
return
}

throw Error.keychainStatus(status)
}

/// Setup the keychain query dictionary used to access the keychain on iOS for a specified key name. Takes into account the Service Name and Access Group if one is set.
Expand Down
16 changes: 8 additions & 8 deletions SwiftKeychainWrapper/KeychainWrapperSubscript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import CoreGraphics
public extension KeychainWrapper {

func remove(forKey key: Key) {
removeObject(forKey: key.rawValue)
try? removeObject(forKey: key.rawValue)
}

}
Expand All @@ -26,39 +26,39 @@ public extension KeychainWrapper {
get { return string(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

subscript(key: Key) -> Bool? {
get { return bool(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

subscript(key: Key) -> Int? {
get { return integer(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

subscript(key: Key) -> Double? {
get { return double(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

subscript(key: Key) -> Float? {
get { return float(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

Expand All @@ -68,7 +68,7 @@ public extension KeychainWrapper {
set {
guard let cgValue = newValue else { return }
let value = Float(cgValue)
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}
#endif
Expand All @@ -77,7 +77,7 @@ public extension KeychainWrapper {
get { return data(forKey: key) }
set {
guard let value = newValue else { return }
set(value, forKey: key.rawValue)
try? set(value, forKey: key.rawValue)
}
}

Expand Down
Loading