Skip to content

Commit

Permalink
Update 0.2.0
Browse files Browse the repository at this point in the history
Changes:
* Added Update method.
* Changed load method.
  • Loading branch information
pkrll committed May 27, 2016
1 parent 54d6464 commit c176498
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 35 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Keychain.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@

Pod::Spec.new do |s|
s.name = 'Keychain'
s.version = '0.1.1'
s.version = '0.2.0'
s.summary = 'Keychain Wrapper Class.'
s.description = <<-DESC
TODO: Add long description of the pod here.
Save and load from the Keychain - the easy way.
DESC
s.homepage = 'https://github.com/pkrll/Keychain'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Ardalan Samimi' => 'ardalan@saturnfive.se' }
s.source = { :git => 'https://github.com/pkrll/Keychain.git', :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/pkrll'
s.social_media_url = 'https://twitter.com/ardalansamimi'
s.ios.deployment_target = '8.0'
s.source_files = 'Keychain/Classes/**/*'
s.frameworks = 'Foundation'
Expand Down
45 changes: 36 additions & 9 deletions Keychain/Classes/Keychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,51 @@ public struct Keychain {
*
* This method allows for more advanced usage and requires a valid attributes dictionary.
* - Parameters:
* - attributes: A dictionary containing an item class specification and optional attributes for controlling the search.
* - query: A dictionary containing an item class specification and optional attributes for controlling the search.
* - SeeAlso: [Keychain Services Reference](xcdoc://?url=developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/doc/constant_group/Attribute_Item_Keys)
* - Returns: A tuple with three members, reflecting the status of the operation and the data fetched, if any.
*/
public static func load(attributes: [String: AnyObject]) -> (success: Bool, statusCode: OSStatus, data: AnyObject?) {
let result = secItemCopy(attributes)
public static func load(query: [String: AnyObject]) -> (success: Bool, statusCode: OSStatus, data: AnyObject?) {
let result = secItemCopy(query)

return (success: (result.status == errSecSuccess), statusCode: result.status, data: result.data)
}
/**
* Update an item in the keychain.
*
* This method allows for more advanced usage and requires a valid attributes dictionary.
* - Parameters:
* - query: A dictionary containing an item class specification and optional attributes for controlling the search. Specify the items whose values you wish to change.
* - attributes: A dictionary containing the attributes whose values should be changed, along with the new values. Only real keychain attributes are permitted in this dictionary (no "meta" attributes are allowed.).
* - SeeAlso: [Keychain Services Reference](xcdoc://?url=developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/doc/constant_group/Attribute_Item_Keys)
* - Returns: A tuple with three members, reflecting the status of the operation and the data fetched, if any.
*/
public static func update(query: [String: AnyObject], attributes: [String: AnyObject]) -> (success: Bool, statusCode: OSStatus) {
let result = secItemUpdate(query, attributes: attributes)
return (success: (result == noErr), statusCode: result)
}
/**
* Delete an item from the Keychain.
*
* This method allows for more advanced usage and requires a valid attributes dictionary.
* - Parameters:
* - query: A dictionary containing an item class specification and optional attributes for controlling the search.
* - SeeAlso: [Keychain Services Reference](xcdoc://?url=developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html#//apple_ref/doc/constant_group/Attribute_Item_Keys)
* - Returns: A tuple with two members, reflecting the status of the operation.
*/
public static func delete(query: [String: AnyObject]) -> (success: Bool, statusCode: OSStatus) {
let result = secItemDelete(query)
return (success: (result == noErr), statusCode: result)
}

}

private extension Keychain {

static func secItemCopy(attributes: [String: AnyObject]) -> (status: OSStatus, data: AnyObject?) {
static func secItemCopy(query: [String: AnyObject]) -> (status: OSStatus, data: AnyObject?) {
var result: AnyObject?
let status: OSStatus = withUnsafeMutablePointer(&result) {
SecItemCopyMatching(attributes as CFDictionaryRef, UnsafeMutablePointer($0))
SecItemCopyMatching(query as CFDictionaryRef, UnsafeMutablePointer($0))
}

return (status, result)
Expand All @@ -116,12 +143,12 @@ private extension Keychain {
return SecItemAdd(attributes, nil)
}

static func secItemUpdate(attributes: [String: AnyObject]) {

static func secItemUpdate(query: [String: AnyObject], attributes: [String: AnyObject]) -> OSStatus {
return SecItemUpdate(query, attributes)
}

static func secItemDelete(attributes: [String: AnyObject]) -> OSStatus {
return SecItemDelete(attributes as CFDictionaryRef)
static func secItemDelete(query: [String: AnyObject]) -> OSStatus {
return SecItemDelete(query as CFDictionaryRef)
}

}
73 changes: 60 additions & 13 deletions Keychain/Classes/KeychainItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import Foundation

public class KeychainItem {

// MARK: - Public Properties

/**
* The value to save.
*/
Expand Down Expand Up @@ -222,39 +225,65 @@ public class KeychainItem {
* The OSStatus code returned.
*/
public var OSStatusCode: OSStatus?

// MARK: - Read Only Properties

/**
* The item class value (read only)
* - Note: This property is set upon initialization.
*/
private(set) var itemClass: String
public private(set) var itemClass: String
/**
* The attributes dictionary (read only).
*/
private(set) var attributes: [String: AnyObject] = [:]
public private(set) var attributes: [String: AnyObject] = [:]
/**
* The search query (read only).
*
* - Note: You can add items with the load(_:) method.
*/
private(set) var searchQuery: [String: AnyObject] = [
kSecMatchLimit as String : kSecMatchLimitOne,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
public private(set) var searchQuery: [String: AnyObject] = [
kSecAttrSynchronizable as String: kSecAttrSynchronizableAny
]

// MARK: - Init

/**
* Creates an instance of the Keychain Item structure.
* - Parameters:
* - withItemClass: The item class of the Keychain item. A KeychainItemClass enum.
*/
public init(withItemClass itemClass: KeychainItemClass) {
self.itemClass = itemClass.rawValue
self.configure()
}

public init(withItemClass itemClass: KeychainItemClass, attributeDictionary: [String: AnyObject]) {
self.itemClass = itemClass.rawValue
self.attributes = attributeDictionary
self.configure()
}

public init(attributeDictionary: [String: AnyObject]) {
self.itemClass = attributeDictionary[kSecClass as String] as? String ?? KeychainItemClass.GenericPassword.rawValue
self.attributes = attributeDictionary
self.configure()
}

private func configure() {
self.searchQuery[kSecClass as String] = self.itemClass as String

if let account = self.account {
self.searchQuery[kSecAttrAccount as String] = account
}

if let service = self.service {
self.searchQuery[kSecAttrService as String] = service
}
}

// MARK: - Edit Methods

/**
* Save the item to the Keychain.
* - Note: Before saving, the properties *account* and *service* should be set.
Expand All @@ -275,25 +304,43 @@ public class KeychainItem {

return result.success
}

public func update(value: String? = nil) -> Bool {
if let value = value {
self.value = value
}

let result = Keychain.update(self.searchQuery, attributes: self.attributes)
self.OSStatusCode = result.statusCode

print(self.attributes)
print(self.searchQuery)

return result.success
}

/**
* Load the item from the Keychain.
*
* - Note: The account and service attributes must be set before loading.
*/
public func load(withExtraAttributes attributes: [String: AnyObject]? = nil) -> Bool {
public func load(withQuery query: [String: AnyObject]? = nil) -> Bool {
guard let account = self.account, let service = self.service else {
return false
}

self.searchQuery[kSecClass as String] = self.itemClass as String
self.searchQuery[kSecAttrAccount as String] = account
self.searchQuery[kSecAttrService as String] = service
var searchQuery = self.searchQuery
searchQuery[kSecMatchLimit as String] = kSecMatchLimitOne
searchQuery[kSecReturnData as String] = kCFBooleanTrue
searchQuery[kSecReturnAttributes as String] = kCFBooleanTrue

if let queryItems = attributes {
if let queryItems = query {
for (key, value) in queryItems {
self.searchQuery[key] = value
searchQuery[key] = value
}
}

let result = Keychain.load(self.searchQuery)
let result = Keychain.load(searchQuery)

if let data = result.data as? [String: AnyObject] {
self.attributes = data
Expand Down
12 changes: 2 additions & 10 deletions Keychain/Classes/KeychainItemFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,15 @@ import Foundation

public struct KeychainItemFactory {

public static func load(itemClass: KeychainItemClass = .GenericPassword) -> [KeychainItem] {
let query: [String: AnyObject] = [
kSecClass as String : itemClass.rawValue as String,
kSecMatchLimit as String : kSecMatchLimitAll,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
kSecAttrSynchronizable as String : kSecAttrSynchronizableAny
]

public static func load(query: [String: AnyObject]) -> [KeychainItem] {
let result = Keychain.load(query)
var items: [KeychainItem] = []

if result.success {
if let array = result.data as? NSArray {
for dict in array {
if dict is [String : AnyObject] {
let item = KeychainItem(withItemClass: .InternetPassword, attributeDictionary: dict as! [String : AnyObject])
let item = KeychainItem(attributeDictionary: dict as! [String : AnyObject])
items.append(item)
}
}
Expand Down

0 comments on commit c176498

Please sign in to comment.