Compare commits

...

5 Commits

Author SHA1 Message Date
matthewpalmer c8a14a87b0 Attempt to fix travis build 2015-01-28 21:04:18 +11:00
matthewpalmer 73074c7755 Remove old files
Cleanup for release
2015-01-28 21:02:08 +11:00
matthewpalmer 480c51f0bc Update pods to specify 1.2.0 2015-01-28 21:00:31 +11:00
matthewpalmer 780e9afe24 Pre push commit 2015-01-28 20:50:33 +11:00
matthewpalmer a64c1d9dc7 Updated to allow for default services.
Note that this commit comes after a gruelling battle with git and Xcode,
so stuff might get weird
2015-01-28 20:46:17 +11:00
19 changed files with 5479 additions and 5832 deletions
+1 -1
View File
@@ -11,5 +11,5 @@ language: objective-c
install:
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
script:
- set -o pipefail && xcodebuild test -workspace Example/Locksmith.xcworkspace -scheme Locksmith-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
- set -o pipefail && xcodebuild test -workspace Example/Locksmith.xcworkspace -scheme Locksmith -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
- pod lib lint --quick
+2 -2
View File
@@ -1,6 +1,6 @@
PODS:
- Expecta (0.3.1)
- Locksmith (0.1.0)
- Locksmith (1.2.0)
- Specta (0.2.1)
DEPENDENCIES:
@@ -14,7 +14,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
Locksmith: b9371e929ae783d9ee80af42278258e9cedda7f6
Locksmith: 392ae330eb455e824dab5b714b6719eb29ef5038
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
COCOAPODS: 0.36.0.beta.1
+1 -1
View File
@@ -9,7 +9,7 @@
Pod::Spec.new do |s|
s.name = "Locksmith"
s.version = "1.1.0"
s.version = "1.2.0"
s.summary = "Locksmith is a sane way to work with the iOS Keychain in Swift."
s.description = <<-DESC
Locksmith is a sane way to work with the iOS Keychain in Swift.
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "Locksmith",
"version": "1.1.0",
"version": "1.2.0",
"summary": "Locksmith is a sane way to work with the iOS Keychain in Swift.",
"description": " Locksmith is a sane way to work with the iOS Keychain in Swift.\n It provides a fast and intuitive way to work with the C Keychain API.\n Results are provided as tuples, and errors are informative and easily detected.\n",
"homepage": "https://github.com/matthewpalmer/Locksmith",
@@ -10,7 +10,7 @@
},
"source": {
"git": "https://github.com/matthewpalmer/Locksmith.git",
"tag": "1.1.0"
"tag": "1.2.0"
},
"social_media_url": "https://twitter.com/_matthewpalmer",
"platforms": {
+2 -2
View File
@@ -1,6 +1,6 @@
PODS:
- Expecta (0.3.1)
- Locksmith (0.1.0)
- Locksmith (1.2.0)
- Specta (0.2.1)
DEPENDENCIES:
@@ -14,7 +14,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
Locksmith: b9371e929ae783d9ee80af42278258e9cedda7f6
Locksmith: 392ae330eb455e824dab5b714b6719eb29ef5038
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
COCOAPODS: 0.36.0.beta.1
+5368 -5368
View File
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>1.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -8,7 +8,7 @@
// Locksmith
#define COCOAPODS_POD_AVAILABLE_Locksmith
#define COCOAPODS_VERSION_MAJOR_Locksmith 0
#define COCOAPODS_VERSION_MINOR_Locksmith 1
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
#define COCOAPODS_VERSION_MINOR_Locksmith 2
#define COCOAPODS_VERSION_PATCH_Locksmith 0
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>1.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -14,8 +14,8 @@
// Locksmith
#define COCOAPODS_POD_AVAILABLE_Locksmith
#define COCOAPODS_VERSION_MAJOR_Locksmith 0
#define COCOAPODS_VERSION_MINOR_Locksmith 1
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
#define COCOAPODS_VERSION_MINOR_Locksmith 2
#define COCOAPODS_VERSION_PATCH_Locksmith 0
// Specta
+34 -17
View File
@@ -9,6 +9,10 @@ import UIKit
import XCTest
import Locksmith
let myService = "myService"
let sampleData = ["key": "value"]
let myUserAccount = "myUserAccount"
class LocksmithTests: XCTestCase {
override func setUp() {
@@ -22,51 +26,64 @@ class LocksmithTests: XCTestCase {
super.tearDown()
}
// public class func saveData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError?
func testSaveData_Once() {
var error = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "myUserAccount")
let error = Locksmith.saveData(["key": "value"], forUserAccount: myUserAccount, inService: myService)
XCTAssert(error == nil, "❌: saving data")
}
func testSaveData_Multiple() {
var errors: [NSError?] = []
for i in 0...10 {
errors.append(Locksmith.saveData(["key": "value \(i)"], inService: "myService", forUserAccount: "myAccount\(i)"))
errors.append(Locksmith.saveData(["key": "value \(i)"], forUserAccount: "myAccount\(i)", inService: "myService"))
}
XCTAssert(errors.filter({ $0 != nil }).isEmpty, "❌: saving multiple items")
}
func testSaveData_Duplicate() {
// Should be successful
let error1 = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user")
let error1 = Locksmith.saveData(sampleData, forUserAccount: "user", inService: myService)
// Should fail
let error2 = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user")
let error2 = Locksmith.saveData(sampleData, forUserAccount: "user", inService: "myService")
XCTAssert(error1 == nil && error2 != nil, "❌: saving duplicate data")
}
// Test using default values for the service
func testWorkflow_Defaults() {
let error1 = Locksmith.saveData(sampleData, forUserAccount: "me")
let error2 = Locksmith.saveData(sampleData, forUserAccount: "me2")
XCTAssert(error1 == nil && error2 == nil, "❌: saving with default service")
let (dict1, err1) = Locksmith.loadDataForUserAccount("me")
let (dict2, err2) = Locksmith.loadDataForUserAccount("me2")
XCTAssert(dict1 != nil && dict2 != nil && err1 == nil && err2 == nil, "❌: loading with default service")
}
// Setup the keychain for requests that use pre-existing values on the keychain (update, read, delete)
func setupLoads() {
Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user1")
Locksmith.saveData(["anotherkey": "anothervalue"], inService: "myService", forUserAccount: "user2")
Locksmith.saveData(["word": "definition"], inService: "myService", forUserAccount: "user3")
Locksmith.saveData(["key": "value"], forUserAccount: "user1", inService: "myService")
Locksmith.saveData(["anotherkey": "anothervalue"], forUserAccount: "user2", inService: "myService")
Locksmith.saveData(["word": "definition"], forUserAccount: "user3", inService: "myService")
}
// public class func loadDataInService(service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?)
func testLoadData_Once() {
setupLoads()
let (dictionary, error) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
let (dictionary, error) = Locksmith.loadDataForUserAccount("user1", inService: "myService")
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading one item")
}
func testLoadData_Multiple() {
setupLoads()
let (dictionary, error) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
let (dictionary2, error2) = Locksmith.loadDataInService("myService", forUserAccount: "user2")
let (dictionary3, error3) = Locksmith.loadDataInService("myService", forUserAccount: "user3")
let (dictionary, error) = Locksmith.loadDataForUserAccount("user1", inService: "myService")
let (dictionary2, error2) = Locksmith.loadDataForUserAccount("user2", inService: "myService")
let (dictionary3, error3) = Locksmith.loadDataForUserAccount("user3", inService: "myService")
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading multiple items")
XCTAssert(dictionary2!.valueForKey("anotherkey")! as NSString == "anothervalue" && error == nil, "❌: loading multiple items")
@@ -77,12 +94,12 @@ class LocksmithTests: XCTestCase {
func testUpdateData() {
setupLoads()
let error = Locksmith.updateData(["key": "newvalue"], inService: "myService", forUserAccount: "user1")
let (dictionary, err) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
let error = Locksmith.updateData(["key": "newvalue"], forUserAccount: "user1", inService: "myService")
let (dictionary, err) = Locksmith.loadDataForUserAccount("user1", inService: "myService")
XCTAssert(dictionary!.valueForKey("key")! as NSString == "newvalue" && error == nil, "❌: updating item")
// Updating an item that doesn't exist should create that item (i.e. performs a regular create request)
let error2 = Locksmith.updateData(["key": "anothervalue"], inService: "myService", forUserAccount: "user1")
let error2 = Locksmith.updateData(["key": "anothervalue"], forUserAccount: "user1", inService: "myService")
XCTAssert(error2 == nil, "❌: updating item that doesn't exist")
}
@@ -90,10 +107,10 @@ class LocksmithTests: XCTestCase {
func testDeleteData() {
setupLoads()
let error = Locksmith.deleteDataInService("myService", forUserAccount: "user1")
let error = Locksmith.deleteDataForUserAccount("user1", inService: "myService")
XCTAssert(error == nil, "❌: deleting existing item")
let error2 = Locksmith.deleteDataInService("myService", forUserAccount: "user1")
let error2 = Locksmith.deleteDataForUserAccount("user1", inService: "myService")
XCTAssert(error2 != nil, "❌: deleting non existent item")
}
+1 -1
View File
@@ -9,7 +9,7 @@
Pod::Spec.new do |s|
s.name = "Locksmith"
s.version = "1.1.0"
s.version = "1.2.0"
s.summary = "Locksmith is a sane way to work with the iOS Keychain in Swift."
s.description = <<-DESC
Locksmith is a sane way to work with the iOS Keychain in Swift.
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.locksmith.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
-19
View File
@@ -1,19 +0,0 @@
//
// Locksmith.h
// Locksmith
//
// Created by Michael Hahn on 12/22/14.
// Copyright (c) 2014 Mathew Palmer. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for Locksmith.
FOUNDATION_EXPORT double LocksmithVersionNumber;
//! Project version string for Locksmith.
FOUNDATION_EXPORT const unsigned char LocksmithVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Locksmith/PublicHeader.h>
-303
View File
@@ -1,303 +0,0 @@
//
// Locksmith.swift
// Locksmith-Demo
//
// Created by Matthew Palmer on 26/10/2014.
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
import UIKit
public let LocksmithErrorDomain = "com.locksmith.error"
public class Locksmith: NSObject {
// MARK: Perform request
class func performRequest(request: LocksmithRequest) -> (NSDictionary?, NSError?) {
let type = request.type
//var result: Unmanaged<AnyObject>? = nil
var result: AnyObject?
var status: OSStatus?
var parsedRequest: NSMutableDictionary = parseRequest(request)
var requestReference = parsedRequest as CFDictionaryRef
switch type {
case .Create:
status = withUnsafeMutablePointer(&result) { SecItemAdd(requestReference, UnsafeMutablePointer($0)) }
case .Read:
status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(requestReference, UnsafeMutablePointer($0)) }
case .Delete:
status = SecItemDelete(requestReference)
case .Update:
status = Locksmith.performUpdate(requestReference, result: &result)
default:
status = nil
}
if let status = status {
var statusCode = Int(status)
let error = Locksmith.keychainError(forCode: statusCode)
var resultsDictionary: NSDictionary?
if result != nil {
if type == .Read && status == errSecSuccess {
if let data = result as? NSData {
// Convert the retrieved data to a dictionary
resultsDictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary
}
}
}
return (resultsDictionary, error)
} else {
let code = LocksmithErrorCode.TypeNotFound.rawValue
let message = internalErrorMessage(forCode: code)
return (nil, NSError(domain: LocksmithErrorDomain, code: code, userInfo: ["message": message]))
}
}
private class func performUpdate(request: CFDictionaryRef, inout result: AnyObject?) -> OSStatus {
// We perform updates to the keychain by first deleting the matching object, then writing to it with the new value.
SecItemDelete(request)
// Even if the delete request failed (e.g. if the item didn't exist before), still try to save the new item.
// If we get an error saving, we'll tell the user about it.
var status: OSStatus = withUnsafeMutablePointer(&result) { SecItemAdd(request, UnsafeMutablePointer($0)) }
return status
}
// MARK: Error Lookup
enum ErrorMessage: String {
case Allocate = "Failed to allocate memory."
case AuthFailed = "Authorization/Authentication failed."
case Decode = "Unable to decode the provided data."
case Duplicate = "The item already exists."
case InteractionNotAllowed = "Interaction with the Security Server is not allowed."
case NoError = "No error."
case NotAvailable = "No trust results are available."
case NotFound = "The item cannot be found."
case Param = "One or more parameters passed to the function were not valid."
case Unimplemented = "Function or operation not implemented."
}
enum LocksmithErrorCode: Int {
case RequestNotSet = 1
case TypeNotFound = 2
case UnableToClear = 3
}
enum LocksmithErrorMessage: String {
case RequestNotSet = "keychainRequest was not set."
case TypeNotFound = "The type of request given was undefined."
case UnableToClear = "Unable to clear the keychain"
}
class func keychainError(forCode statusCode: Int) -> NSError? {
var error: NSError?
if statusCode != Int(errSecSuccess) {
let message = errorMessage(statusCode)
// println("Keychain request failed. Code: \(statusCode). Message: \(message)")
error = NSError(domain: LocksmithErrorDomain, code: statusCode, userInfo: ["message": message])
}
return error
}
// MARK: Private methods
private class func internalErrorMessage(forCode statusCode: Int) -> NSString {
switch statusCode {
case LocksmithErrorCode.RequestNotSet.rawValue:
return LocksmithErrorMessage.RequestNotSet.rawValue
case LocksmithErrorCode.UnableToClear.rawValue:
return LocksmithErrorMessage.UnableToClear.rawValue
default:
return "Error message for code \(statusCode) not set"
}
}
private class func parseRequest(request: LocksmithRequest) -> NSMutableDictionary {
var parsedRequest = NSMutableDictionary()
var options = [String: AnyObject?]()
options[String(kSecAttrAccount)] = request.userAccount
options[String(kSecAttrAccessGroup)] = request.group
options[String(kSecAttrService)] = request.service
options[String(kSecAttrSynchronizable)] = request.synchronizable
options[String(kSecClass)] = securityCode(request.securityClass)
for (key, option) in options {
parsedRequest.setOptional(option, forKey: key)
}
switch request.type {
case .Create:
parsedRequest = parseCreateRequest(request, inDictionary: parsedRequest)
case .Delete:
parsedRequest = parseDeleteRequest(request, inDictionary: parsedRequest)
case .Update:
parsedRequest = parseCreateRequest(request, inDictionary: parsedRequest)
default: // case .Read:
parsedRequest = parseReadRequest(request, inDictionary: parsedRequest)
}
return parsedRequest
}
private class func parseCreateRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
if let data = request.data {
let encodedData = NSKeyedArchiver.archivedDataWithRootObject(data)
dictionary.setObject(encodedData, forKey: String(kSecValueData))
}
return dictionary
}
private class func parseReadRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
dictionary.setOptional(kCFBooleanTrue, forKey: String(kSecReturnData))
switch request.matchLimit {
case .One:
dictionary.setObject(kSecMatchLimitOne, forKey: String(kSecMatchLimit))
case .Many:
dictionary.setObject(kSecMatchLimitAll, forKey: String(kSecMatchLimit))
}
return dictionary
}
private class func parseDeleteRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
return dictionary
}
private class func errorMessage(code: Int) -> NSString {
switch code {
case Int(errSecAllocate):
return ErrorMessage.Allocate.rawValue
case Int(errSecAuthFailed):
return ErrorMessage.AuthFailed.rawValue
case Int(errSecDecode):
return ErrorMessage.Decode.rawValue
case Int(errSecDuplicateItem):
return ErrorMessage.Duplicate.rawValue
case Int(errSecInteractionNotAllowed):
return ErrorMessage.InteractionNotAllowed.rawValue
case Int(errSecItemNotFound):
return ErrorMessage.NotFound.rawValue
case Int(errSecNotAvailable):
return ErrorMessage.NotAvailable.rawValue
case Int(errSecParam):
return ErrorMessage.Param.rawValue
case Int(errSecSuccess):
return ErrorMessage.NoError.rawValue
case Int(errSecUnimplemented):
return ErrorMessage.Unimplemented.rawValue
default:
return "Undocumented error with code \(code)."
}
}
private class func securityCode(securityClass: SecurityClass) -> CFStringRef {
switch securityClass {
case .GenericPassword:
return kSecClassGenericPassword
case .Certificate:
return kSecClassCertificate
case .Identity:
return kSecClassIdentity
case .InternetPassword:
return kSecClassInternetPassword
case .Key:
return kSecClassKey
default:
return kSecClassGenericPassword
}
}
}
// MARK: Convenient Class Methods
extension Locksmith {
public class func saveData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Create, data: data)
let (dictionary, error) = Locksmith.performRequest(saveRequest)
return error
}
public class func loadDataInService(service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
let readRequest = LocksmithRequest(service: service, userAccount: userAccount)
return Locksmith.performRequest(readRequest)
}
public class func deleteDataInService(service: String, forUserAccount userAccount: String) -> NSError? {
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Delete)
let (dictionary, error) = Locksmith.performRequest(deleteRequest)
return error
}
public class func updateData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let updateRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Update, data: data)
let (dictionary, error) = Locksmith.performRequest(updateRequest)
return error
}
public class func clearKeychain() -> NSError? {
// Delete all of the keychain data of the given class
func deleteDataForSecClass(secClass: CFTypeRef) -> NSError? {
var request = NSMutableDictionary()
request.setObject(secClass, forKey: String(kSecClass))
var status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
if let status = status {
var statusCode = Int(status)
return Locksmith.keychainError(forCode: statusCode)
}
return nil
}
// For each of the sec class types, delete all of the saved items of that type
let classes = [kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity]
let errors: [NSError?] = classes.map({
return deleteDataForSecClass($0)
})
// Remove those that were successful, or failed with an acceptable error code
let filtered = errors.filter({
if let error = $0 {
// There was an error
// If the error indicates that there was no item with that sec class, that's fine.
// Some of the sec classes will have nothing in them in most cases.
return error.code != Int(errSecItemNotFound) ? true : false
}
// There was no error
return false
})
// If the filtered array is empty, then everything went OK
if filtered.isEmpty {
return nil
}
// At least one of the delete operations failed
let code = LocksmithErrorCode.UnableToClear.rawValue
let message = internalErrorMessage(forCode: code)
return NSError(domain: LocksmithErrorDomain, code: code, userInfo: ["message": message])
}
}
// MARK: Dictionary Extensions
extension NSMutableDictionary {
func setOptional(optional: AnyObject?, forKey key: NSCopying) {
if let object: AnyObject = optional {
self.setObject(object, forKey: key)
}
}
}
-56
View File
@@ -1,56 +0,0 @@
//
// LocksmithRequest.swift
// Locksmith-Demo
//
// Created by Matthew Palmer on 26/10/2014.
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
import UIKit
public enum SecurityClass: Int {
case GenericPassword, InternetPassword, Certificate, Key, Identity
}
public enum MatchLimit: Int {
case One, Many
}
public enum RequestType: Int {
case Create, Read, Update, Delete
}
public class LocksmithRequest: NSObject, DebugPrintable {
// Keychain Options
// Required
var service: String
var userAccount: String
var type: RequestType = .Read // Default to non-destructive
// Optional
var securityClass: SecurityClass = .GenericPassword // Default to password lookup
var group: String?
var data: NSDictionary?
var matchLimit: MatchLimit = .One
var synchronizable = false
// Debugging
override public var debugDescription: String {
return "service: \(self.service), type: \(self.type.rawValue), userAccount: \(self.userAccount)"
}
required public init(service: String, userAccount: String) {
self.service = service
self.userAccount = userAccount
}
convenience init(service: String, userAccount: String, requestType: RequestType) {
self.init(service: service, userAccount: userAccount)
self.type = requestType
}
convenience init(service: String, userAccount: String, requestType: RequestType, data: NSDictionary) {
self.init(service: service, userAccount: userAccount, requestType: requestType)
self.data = data
}
}
+10 -8
View File
@@ -9,6 +9,8 @@ import UIKit
import Security
public let LocksmithErrorDomain = "com.locksmith.error"
public let LocksmithDefaultService = NSBundle.mainBundle().infoDictionary![kCFBundleIdentifierKey] as String
public class Locksmith: NSObject {
// MARK: Perform request
@@ -222,25 +224,25 @@ public class Locksmith: NSObject {
// MARK: Convenient Class Methods
extension Locksmith {
public class func saveData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Create, data: data)
public class func saveData(data: Dictionary<String, String>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) -> NSError? {
let saveRequest = LocksmithRequest(userAccount: userAccount, requestType: .Create, data: data, service: service)
let (dictionary, error) = Locksmith.performRequest(saveRequest)
return error
}
public class func loadDataInService(service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
let readRequest = LocksmithRequest(service: service, userAccount: userAccount)
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> (NSDictionary?, NSError?) {
let readRequest = LocksmithRequest(userAccount: userAccount, service: service)
return Locksmith.performRequest(readRequest)
}
public class func deleteDataInService(service: String, forUserAccount userAccount: String) -> NSError? {
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Delete)
public class func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> NSError? {
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
let (dictionary, error) = Locksmith.performRequest(deleteRequest)
return error
}
public class func updateData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let updateRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Update, data: data)
public class func updateData(data: Dictionary<String, String>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) -> NSError? {
let updateRequest = LocksmithRequest(userAccount: userAccount, requestType: .Update, data: data, service: service)
let (dictionary, error) = Locksmith.performRequest(updateRequest)
return error
}
+6 -6
View File
@@ -23,7 +23,7 @@ public enum RequestType: Int {
public class LocksmithRequest: NSObject, DebugPrintable {
// Keychain Options
// Required
var service: String
var service: String = NSBundle.mainBundle().infoDictionary![kCFBundleIdentifierKey] as String // Default to Bundle Identifier
var userAccount: String
var type: RequestType = .Read // Default to non-destructive
@@ -39,18 +39,18 @@ public class LocksmithRequest: NSObject, DebugPrintable {
return "service: \(self.service), type: \(self.type.rawValue), userAccount: \(self.userAccount)"
}
required public init(service: String, userAccount: String) {
required public init(userAccount: String, service: String = LocksmithDefaultService) {
self.service = service
self.userAccount = userAccount
}
convenience init(service: String, userAccount: String, requestType: RequestType) {
self.init(service: service, userAccount: userAccount)
convenience init(userAccount: String, requestType: RequestType, service: String = LocksmithDefaultService) {
self.init(userAccount: userAccount, service: service)
self.type = requestType
}
convenience init(service: String, userAccount: String, requestType: RequestType, data: NSDictionary) {
self.init(service: service, userAccount: userAccount, requestType: requestType)
convenience init(userAccount: String, requestType: RequestType, data: NSDictionary, service: String = LocksmithDefaultService) {
self.init(userAccount: userAccount, requestType: requestType, service: service)
self.data = data
}
}
+46 -14
View File
@@ -2,42 +2,73 @@
A sane way to work with the iOS Keychain in Swift.
<!--[![CI Status](http://img.shields.io/travis/matthewpalmer/Locksmith.svg?style=flat)](https://travis-ci.org/matthewpalmer/Locksmith)-->
[![CI Status](http://img.shields.io/travis/matthewpalmer/Locksmith.svg?style=flat)](https://travis-ci.org/matthewpalmer/Locksmith)
[![Version](https://img.shields.io/cocoapods/v/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
[![License](https://img.shields.io/cocoapods/l/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
[![Platform](https://img.shields.io/cocoapods/p/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
## Installation
### CocoaPods
Locksmith is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
pod "Locksmith"
pod "Locksmith"
### Manual
Alternatively, you can simply drag the two files `Locksmith.swift` and `LocksmithRequest.swift` into your project.
## Quick Start
**Save Data**
In the following examples, you can choose not to provide a value for the `inService` parameter, and it will default to your Bundle Identifier.
**Save data**
```swift
Locksmith.saveData(["some key": "some value"], inService: "myService", forUserAccount: "myUserAccount")
let error = Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")
```
**Load Data**
**Save data, specifying a service**
```swift
let (dictionary, error) = Locksmith.loadData(inService: "myService", forUserAccount: "myUserAccount")
let error = Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount", inService: "myService")
```
**Update Data**
**Load data**
```swift
Locksmith.updateData(["some key": "another value"], inService: "myService", forUserAccount: "myUserAccount")
let (dictionary, error) = Locksmith.loadDataForUserAccount("myUserAccount")
```
**Delete Data**
**Load data, specifying a service**
```swift
Locksmith.deleteData(inService: "myService", forUserAccount: "myUserAccount")
let (dictionary, error) = Locksmith.loadDataForUserAccount("myUserAccount", inService: "myService")
```
**Update data**
```swift
let error = Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount")
```
**Update data, specifying a service**
```swift
let error = Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount", inService: "myService")
```
**Delete data**
```swift
let error = Locksmith.deleteDataForUserAccount("myUserAccount")
```
**Delete data, specifying a service**
```swift
let error = Locksmith.deleteDataForUserAccount("myUserAccount", inService: "myService")
```
## Custom Requests
@@ -45,7 +76,8 @@ To create custom keychain requests, you first have to instantiate a `LocksmithRe
**Saving**
```swift
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, data: ["some key": "some value"])
// As above, the `service` parameter will default to your Bundle Identifier if omitted.
let saveRequest = LocksmithRequest(userAccount: userAccount, data: ["some key": "some value"], service: service)
// Customize the request
saveRequest.synchronizable = true
Locksmith.performRequest(saveRequest)
@@ -53,13 +85,13 @@ Locksmith.performRequest(saveRequest)
**Reading**
```swift
let readRequest = LocksmithRequest(service: service, userAccount: userAccount)
let readRequest = LocksmithRequest(userAccount: userAccount, service: service)
let (dictionary, error) = Locksmith.performRequest(readRequest)
```
**Deleting**
```swift
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Delete)
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
Locksmith.performRequest(deleteRequest)
```
@@ -90,4 +122,4 @@ var synchronizable: Bool // Defaults to false
## License
Locksmith is available under the MIT license. See the LICENSE file for more info.
Locksmith is available under the MIT license. See the LICENSE file for more info.