Compare commits

..

7 Commits

Author SHA1 Message Date
matthewpalmer 02cfb487cc Adds default values for the service parameter.
Note that this isn't working yet. Cocoapods isn't replciating the
changes in the demo/test app, so I'm hoping this push will make it
behave properly.
2015-01-27 19:53:54 +11:00
matthewpalmer 8f83bda24d Merge branch '1.1.1' into getaaron-default-service
* 1.1.1:
  Remove old files
  Update podspec version
  Update readme with alternative installation instructions

Conflicts:
	Locksmith/Locksmith.swift
	Locksmith/LocksmithRequest.swift
2015-01-26 08:19:56 +11:00
matthewpalmer e620b5d94a Renamed and reordered parameters to allow for default values 2015-01-26 08:19:06 +11:00
matthewpalmer cbcc56e701 Remove old files 2015-01-26 07:52:08 +11:00
Aaron Brager ee336f119f Provide a default service 2015-01-25 11:04:48 -08:00
matthewpalmer cd3a63e589 Update podspec version 2015-01-25 21:08:23 +11:00
matthewpalmer 1371062131 Update readme with alternative installation instructions 2015-01-25 21:07:28 +11:00
34 changed files with 7740 additions and 3074 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
# * http://www.objc.io/issue-6/travis-ci.html
# * https://github.com/supermarin/xcpretty#usage
language: swift
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
@@ -11,5 +11,5 @@ language: swift
install:
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
script:
- set -o pipefail && xcodebuild test -workspace Example/Locksmith.xcworkspace -scheme Locksmith -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
- set -o pipefail && xcodebuild test -workspace Example/Locksmith.xcworkspace -scheme Locksmith-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
- pod lib lint --quick
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,7 +1,5 @@
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
platform :ios, '8.0'
target 'Locksmith', :exclusive => true do
+6 -6
View File
@@ -1,6 +1,6 @@
PODS:
- Expecta (0.3.1)
- Locksmith (1.2.1)
- Locksmith (1.1.1)
- Specta (0.2.1)
DEPENDENCIES:
@@ -10,11 +10,11 @@ DEPENDENCIES:
EXTERNAL SOURCES:
Locksmith:
:path: "../"
:path: ../
SPEC CHECKSUMS:
Expecta: a354d4633409dd9fe8c4f5ff5130426adbe31628
Locksmith: 770f6c5c6e5c5c712d1f00e709c62b8a7240c716
Specta: 15a276a6343867b426d5ed135d5aa4d04123a573
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
Locksmith: c2b5a219b27d0317b71a7b1a70f0148cee03a9f6
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
COCOAPODS: 0.36.3
COCOAPODS: 0.36.0.beta.1
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "Locksmith",
"version": "1.2.1",
"version": "1.1.1",
"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.2.1"
"tag": "1.1.1"
},
"social_media_url": "https://twitter.com/_matthewpalmer",
"platforms": {
+6 -6
View File
@@ -1,6 +1,6 @@
PODS:
- Expecta (0.3.1)
- Locksmith (1.2.1)
- Locksmith (1.1.1)
- Specta (0.2.1)
DEPENDENCIES:
@@ -10,11 +10,11 @@ DEPENDENCIES:
EXTERNAL SOURCES:
Locksmith:
:path: "../"
:path: ../
SPEC CHECKSUMS:
Expecta: a354d4633409dd9fe8c4f5ff5130426adbe31628
Locksmith: 770f6c5c6e5c5c712d1f00e709c62b8a7240c716
Specta: 15a276a6343867b426d5ed135d5aa4d04123a573
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
Locksmith: c2b5a219b27d0317b71a7b1a70f0148cee03a9f6
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
COCOAPODS: 0.36.3
COCOAPODS: 0.36.0.beta.1
+6211 -1747
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>1.2.1</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -4,7 +4,6 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Locksmith" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Expecta" "${PODS_ROOT}/Headers/Public/Locksmith" "${PODS_ROOT}/Headers/Public/Specta"
OTHER_LDFLAGS = ${PODS_LOCKSMITH_LOCKSMITH_OTHER_LDFLAGS} -ObjC
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
PODS_ROOT = ${SRCROOT}
SKIP_INSTALL = YES
PODS_ROOT = ${SRCROOT}
@@ -9,6 +9,6 @@
// Locksmith
#define COCOAPODS_POD_AVAILABLE_Locksmith
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
#define COCOAPODS_VERSION_MINOR_Locksmith 2
#define COCOAPODS_VERSION_MINOR_Locksmith 1
#define COCOAPODS_VERSION_PATCH_Locksmith 1
@@ -11,7 +11,7 @@ install_framework()
local source="${BUILT_PRODUCTS_DIR}/Pods-Locksmith/$1"
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
if [ -L ${source} ]; then
echo "Symlinked..."
source=$(readlink "${source}")
fi
@@ -28,13 +28,10 @@ install_framework()
local basename
basename=$(echo $1 | sed -E s/\\..+// && exit ${PIPESTATUS[0]})
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/$1/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/$1/${basename}" | grep @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
if [ "${CODE_SIGNING_REQUIRED}" == "YES" ]; then
code_sign "${destination}/${lib}"
fi
echo "rsync -av \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -av "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
done
}
@@ -6,8 +6,6 @@ mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
> "$RESOURCES_TO_COPY"
XCASSET_FILES=""
install_resource()
{
case $1 in
@@ -38,7 +36,6 @@ install_resource()
xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm"
;;
*.xcassets)
XCASSET_FILES="$XCASSET_FILES '${PODS_ROOT}/$1'"
;;
/*)
echo "$1"
@@ -57,7 +54,7 @@ if [[ "${ACTION}" == "install" ]]; then
fi
rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ]
then
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
@@ -73,6 +70,5 @@ then
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
while read line; do XCASSET_FILES="$XCASSET_FILES '$line'"; done <<<$(find "$PWD" -name "*.xcassets" | egrep -v "^$PODS_ROOT")
echo $XCASSET_FILES | xargs actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
fi
@@ -2,8 +2,8 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/Locksmith.framework/Headers"
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Locksmith"
OTHER_LDFLAGS = -ObjC -framework "Locksmith"
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
PODS_ROOT = ${SRCROOT}/Pods
@@ -2,8 +2,8 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/Locksmith.framework/Headers"
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Locksmith"
OTHER_LDFLAGS = -ObjC -framework "Locksmith"
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
PODS_ROOT = ${SRCROOT}/Pods
@@ -5,5 +5,4 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Expecta" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Expecta" "${PODS_ROOT}/Headers/Public/Locksmith" "${PODS_ROOT}/Headers/Public/Specta"
OTHER_LDFLAGS = ${PODS_TESTS_EXPECTA_OTHER_LDFLAGS} -ObjC
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
PODS_ROOT = ${SRCROOT}
SKIP_INSTALL = YES
PODS_ROOT = ${SRCROOT}
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.2.1</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -4,7 +4,6 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Locksmith" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Expecta" "${PODS_ROOT}/Headers/Public/Locksmith" "${PODS_ROOT}/Headers/Public/Specta"
OTHER_LDFLAGS = ${PODS_TESTS_LOCKSMITH_OTHER_LDFLAGS} -ObjC
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
PODS_ROOT = ${SRCROOT}
SKIP_INSTALL = YES
PODS_ROOT = ${SRCROOT}
@@ -5,5 +5,4 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Specta" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Expecta" "${PODS_ROOT}/Headers/Public/Locksmith" "${PODS_ROOT}/Headers/Public/Specta"
OTHER_LDFLAGS = ${PODS_TESTS_SPECTA_OTHER_LDFLAGS} -ObjC
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
PODS_ROOT = ${SRCROOT}
SKIP_INSTALL = YES
PODS_ROOT = ${SRCROOT}
@@ -15,7 +15,7 @@
// Locksmith
#define COCOAPODS_POD_AVAILABLE_Locksmith
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
#define COCOAPODS_VERSION_MINOR_Locksmith 2
#define COCOAPODS_VERSION_MINOR_Locksmith 1
#define COCOAPODS_VERSION_PATCH_Locksmith 1
// Specta
@@ -11,7 +11,7 @@ install_framework()
local source="${BUILT_PRODUCTS_DIR}/Pods-Tests/$1"
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
if [ -L ${source} ]; then
echo "Symlinked..."
source=$(readlink "${source}")
fi
@@ -28,13 +28,10 @@ install_framework()
local basename
basename=$(echo $1 | sed -E s/\\..+// && exit ${PIPESTATUS[0]})
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/$1/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/$1/${basename}" | grep @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
if [ "${CODE_SIGNING_REQUIRED}" == "YES" ]; then
code_sign "${destination}/${lib}"
fi
echo "rsync -av \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -av "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
done
}
@@ -6,8 +6,6 @@ mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
> "$RESOURCES_TO_COPY"
XCASSET_FILES=""
install_resource()
{
case $1 in
@@ -38,7 +36,6 @@ install_resource()
xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm"
;;
*.xcassets)
XCASSET_FILES="$XCASSET_FILES '${PODS_ROOT}/$1'"
;;
/*)
echo "$1"
@@ -57,7 +54,7 @@ if [[ "${ACTION}" == "install" ]]; then
fi
rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ]
then
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
@@ -73,6 +70,5 @@ then
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
while read line; do XCASSET_FILES="$XCASSET_FILES '$line'"; done <<<$(find "$PWD" -name "*.xcassets" | egrep -v "^$PODS_ROOT")
echo $XCASSET_FILES | xargs actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
fi
@@ -2,8 +2,8 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/Expecta.framework/Headers" -iquote "$PODS_FRAMEWORK_BUILD_PATH/Locksmith.framework/Headers" -iquote "$PODS_FRAMEWORK_BUILD_PATH/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
OTHER_LDFLAGS = -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
PODS_ROOT = ${SRCROOT}/Pods
@@ -2,8 +2,8 @@ FRAMEWORK_SEARCH_PATHS = "$PODS_FRAMEWORK_BUILD_PATH"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/Expecta.framework/Headers" -iquote "$PODS_FRAMEWORK_BUILD_PATH/Locksmith.framework/Headers" -iquote "$PODS_FRAMEWORK_BUILD_PATH/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
OTHER_LDFLAGS = -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
PODS_ROOT = ${SRCROOT}/Pods
+45 -48
View File
@@ -2,18 +2,19 @@
// LocksmithTests.swift
// LocksmithTests
//
// Created by Matthew Palmer on 27/06/2015.
// Copyright © 2015 Matthew Palmer. All rights reserved.
// Copyright (c) 2014 Mathew Palmer. All rights reserved.
//
import UIKit
import XCTest
import Locksmith
class LocksmithDemoTests: XCTestCase {
class LocksmithTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
try! Locksmith.clearKeychain()
Locksmith.clearKeychain()
}
override func tearDown() {
@@ -23,90 +24,86 @@ class LocksmithDemoTests: XCTestCase {
// public class func saveData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError?
func testSaveData_Once() {
try! Locksmith.saveData(["key": "value"], forUserAccount: "myUserAccount", inService: "myService")
// XCTAssert(error == nil, ": saving data")
let error = Locksmith.saveData(data: ["key": "value"], forUserAccount: "myUserAccount", inService: "myService")
// var error = Locksmith.saveData(["key": "value"], inService: <#String#>, forUserAccount: <#String#>)
// var error = Locksmith.saveData(, inService: "myService", forUserAccount: "myUserAccount")
XCTAssert(error == nil, "❌: saving data")
}
func testSaveData_Multiple() {
// var errors: [NSError?] = []
var errors: [NSError?] = []
for i in 0...10 {
try! Locksmith.saveData(["key": "value \(i)"], forUserAccount: "myAccount\(i)", inService: "myService")
errors.append(Locksmith.saveData(["key": "value \(i)"], inService: "myService", forUserAccount: "myAccount\(i)"))
}
// XCTAssert(errors.filter({ $0 != nil }).isEmpty, ": saving multiple items")
XCTAssert(errors.filter({ $0 != nil }).isEmpty, "❌: saving multiple items")
}
func testSaveData_Duplicate() {
// Should be successful
try! Locksmith.saveData(["key": "value"], forUserAccount: "user", inService: "myService")
let error1 = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user")
// Should fail
do {
try Locksmith.saveData(["key": "value"], forUserAccount: "user", inService: "myService")
} catch {
XCTAssert(true)
}
let error2 = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user")
XCTAssert(error1 == nil && error2 != nil, "❌: saving duplicate data")
}
// Setup the keychain for requests that use pre-existing values on the keychain (update, read, delete)
func setupLoads() {
try! Locksmith.saveData(["key": "value"], forUserAccount: "user1", inService: "myService")
try! Locksmith.saveData(["anotherkey": "anothervalue"], forUserAccount: "user2", inService: "myService")
try! Locksmith.saveData(["word": "definition"], forUserAccount: "user3", inService: "myService")
Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user1")
Locksmith.saveData(["anotherkey": "anothervalue"], inService: "myService", forUserAccount: "user2")
Locksmith.saveData(["word": "definition"], inService: "myService", forUserAccount: "user3")
}
// public class func loadDataInService(service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?)
func testLoadData_Once() {
setupLoads()
let dictionary = Locksmith.loadDataForUserAccount("user1", inService: "myService")
XCTAssert(dictionary!.valueForKey("key") as! NSString == "value", "❌: loading one item")
let (dictionary, error) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading one item")
}
func testLoadData_Multiple() {
setupLoads()
do {
let dictionary = Locksmith.loadDataForUserAccount("user1", inService: "myService")
let dictionary2 = Locksmith.loadDataForUserAccount("user2", inService: "myService")
let dictionary3 = Locksmith.loadDataForUserAccount("user3", inService: "myService")
XCTAssert(dictionary!.valueForKey("key") as! NSString == "value", "❌: loading multiple items")
XCTAssert(dictionary2!.valueForKey("anotherkey") as! NSString == "anothervalue", "❌: loading multiple items")
XCTAssert(dictionary3!.valueForKey("word") as! NSString == "definition", "❌: loading multiple items")
} catch {
XCTAssert(false)
}
let (dictionary, error) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
let (dictionary2, error2) = Locksmith.loadDataInService("myService", forUserAccount: "user2")
let (dictionary3, error3) = Locksmith.loadDataInService("myService", forUserAccount: "user3")
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading multiple items")
XCTAssert(dictionary2!.valueForKey("anotherkey")! as NSString == "anothervalue" && error == nil, "❌: loading multiple items")
XCTAssert(dictionary3!.valueForKey("word")! as NSString == "definition" && error == nil, "❌: loading multiple items")
}
// public class func updateData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError?
func testUpdateData() {
setupLoads()
try! Locksmith.updateData(["key": "newvalue"], forUserAccount: "user1", inService: "myService")
let dictionary = Locksmith.loadDataForUserAccount("user1", inService: "myService")
XCTAssert(dictionary!.valueForKey("key") as! NSString == "newvalue", "❌: updating item")
let error = Locksmith.updateData(["key": "newvalue"], inService: "myService", forUserAccount: "user1")
let (dictionary, err) = Locksmith.loadDataInService("myService", forUserAccount: "user1")
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)
try! Locksmith.updateData(["key": "anothervalue"], forUserAccount: "user1", inService: "myService")
XCTAssert(true, "❌: updating item that doesn't exist")
let error2 = Locksmith.updateData(["key": "anothervalue"], inService: "myService", forUserAccount: "user1")
XCTAssert(error2 == nil, "❌: updating item that doesn't exist")
}
// public class func deleteDataInService(service: String, forUserAccount userAccount: String) -> NSError?
func testDeleteData() {
setupLoads()
try! Locksmith.deleteDataForUserAccount("user1", inService: "myService")
XCTAssert(true, "❌: deleting existing item")
do {
// Should fail
try Locksmith.deleteDataForUserAccount("user1", inService: "myService")
XCTAssert(false, "❌: did not throw error on deleting non existent item")
} catch {
XCTAssert(true, "❌: deleting non existent item")
}
let error = Locksmith.deleteDataInService("myService", forUserAccount: "user1")
XCTAssert(error == nil, "❌: deleting existing item")
let error2 = Locksmith.deleteDataInService("myService", forUserAccount: "user1")
XCTAssert(error2 != nil, "❌: deleting non existent item")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@
Pod::Spec.new do |s|
s.name = "Locksmith"
s.version = "2.0"
s.version = "1.1.1"
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.
+22 -27
View File
@@ -7,12 +7,11 @@
objects = {
/* Begin PBXBuildFile section */
9D60D8141B10927B00BE14A9 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9D60D8131B10927B00BE14A9 /* Info.plist */; };
9DF0A2C01B0E394F0049F83A /* Locksmith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF0A2BE1B0E394F0049F83A /* Locksmith.swift */; };
9DF0A2C11B0E394F0049F83A /* LocksmithRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF0A2BF1B0E394F0049F83A /* LocksmithRequest.swift */; };
9DF0A2C81B0E3D370049F83A /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9DF0A2C71B0E3D370049F83A /* Info.plist */; };
BFFB19D61A4870A300CCFFC3 /* Locksmith.h in Headers */ = {isa = PBXBuildFile; fileRef = BFFB19D51A4870A300CCFFC3 /* Locksmith.h */; settings = {ATTRIBUTES = (Public, ); }; };
BFFB19DC1A4870A300CCFFC3 /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFB19D01A4870A300CCFFC3 /* Locksmith.framework */; };
BFFB19E31A4870A300CCFFC3 /* LocksmithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFB19E21A4870A300CCFFC3 /* LocksmithTests.swift */; };
BFFB19EE1A4870E400CCFFC3 /* LocksmithRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFB19EC1A4870E400CCFFC3 /* LocksmithRequest.swift */; };
BFFB19EF1A4870E400CCFFC3 /* Locksmith.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFB19ED1A4870E400CCFFC3 /* Locksmith.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -26,13 +25,14 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
9D60D8131B10927B00BE14A9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Pod/Info.plist; sourceTree = SOURCE_ROOT; };
9DF0A2BE1B0E394F0049F83A /* Locksmith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Locksmith.swift; path = Pod/Classes/Locksmith.swift; sourceTree = SOURCE_ROOT; };
9DF0A2BF1B0E394F0049F83A /* LocksmithRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LocksmithRequest.swift; path = Pod/Classes/LocksmithRequest.swift; sourceTree = SOURCE_ROOT; };
9DF0A2C71B0E3D370049F83A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFFB19D01A4870A300CCFFC3 /* Locksmith.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Locksmith.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BFFB19D41A4870A300CCFFC3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFFB19D51A4870A300CCFFC3 /* Locksmith.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Locksmith.h; sourceTree = "<group>"; };
BFFB19DB1A4870A300CCFFC3 /* LocksmithTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LocksmithTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
BFFB19E11A4870A300CCFFC3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFFB19E21A4870A300CCFFC3 /* LocksmithTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocksmithTests.swift; sourceTree = "<group>"; };
BFFB19EC1A4870E400CCFFC3 /* LocksmithRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithRequest.swift; sourceTree = "<group>"; };
BFFB19ED1A4870E400CCFFC3 /* Locksmith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locksmith.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -75,8 +75,9 @@
BFFB19D21A4870A300CCFFC3 /* Locksmith */ = {
isa = PBXGroup;
children = (
9DF0A2BE1B0E394F0049F83A /* Locksmith.swift */,
9DF0A2BF1B0E394F0049F83A /* LocksmithRequest.swift */,
BFFB19EC1A4870E400CCFFC3 /* LocksmithRequest.swift */,
BFFB19ED1A4870E400CCFFC3 /* Locksmith.swift */,
BFFB19D51A4870A300CCFFC3 /* Locksmith.h */,
BFFB19D31A4870A300CCFFC3 /* Supporting Files */,
);
path = Locksmith;
@@ -85,7 +86,7 @@
BFFB19D31A4870A300CCFFC3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
9D60D8131B10927B00BE14A9 /* Info.plist */,
BFFB19D41A4870A300CCFFC3 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -102,7 +103,7 @@
BFFB19E01A4870A300CCFFC3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
9DF0A2C71B0E3D370049F83A /* Info.plist */,
BFFB19E11A4870A300CCFFC3 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -114,6 +115,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
BFFB19D61A4870A300CCFFC3 /* Locksmith.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -162,7 +164,6 @@
BFFB19C71A4870A300CCFFC3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = "Mathew Palmer";
TargetAttributes = {
@@ -197,7 +198,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9D60D8141B10927B00BE14A9 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -205,7 +205,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9DF0A2C81B0E3D370049F83A /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -216,8 +215,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9DF0A2C11B0E394F0049F83A /* LocksmithRequest.swift in Sources */,
9DF0A2C01B0E394F0049F83A /* Locksmith.swift in Sources */,
BFFB19EF1A4870E400CCFFC3 /* Locksmith.swift in Sources */,
BFFB19EE1A4870E400CCFFC3 /* LocksmithRequest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -275,7 +274,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -315,7 +314,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -328,13 +327,11 @@
BFFB19E71A4870A300CCFFC3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = "$(SRCROOT)/Pod/Info.plist";
INFOPLIST_FILE = Locksmith/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -346,13 +343,11 @@
BFFB19E81A4870A300CCFFC3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_TESTABILITY = YES;
INFOPLIST_FILE = "$(SRCROOT)/Pod/Info.plist";
INFOPLIST_FILE = Locksmith/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -372,7 +367,7 @@
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = LockSmithTests/Info.plist;
INFOPLIST_FILE = LocksmithTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -385,7 +380,7 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = LockSmithTests/Info.plist;
INFOPLIST_FILE = LocksmithTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1,110 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19CF1A4870A300CCFFC3"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19DA1A4870A300CCFFC3"
BuildableName = "LocksmithTests.xctest"
BlueprintName = "LocksmithTests"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19DA1A4870A300CCFFC3"
BuildableName = "LocksmithTests.xctest"
BlueprintName = "LocksmithTests"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19CF1A4870A300CCFFC3"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19CF1A4870A300CCFFC3"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFFB19CF1A4870A300CCFFC3"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -13,12 +13,14 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+19
View File
@@ -0,0 +1,19 @@
//
// 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>
-243
View File
@@ -1,243 +0,0 @@
//
// Locksmith.swift
//
// Created by Matthew Palmer on 26/10/2014.
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
@testable import Locksmith
import CoreFoundation
import UIKit
import Security
public let LocksmithDefaultService = NSBundle.mainBundle().infoDictionary![String(kCFBundleIdentifierKey)] as? String ?? "com.locksmith.defaultService"
// MARK: Locksmith Error
public enum LocksmithError: String, ErrorType {
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 RequestNotSet = "The request was not set"
case TypeNotFound = "The type was not found"
case UnableToClear = "Unable to clear the keychain"
case Undefined = "An undefined error occurred"
case Unimplemented = "Function or operation not implemented."
init?(fromStatusCode code: Int) {
switch code {
case Int(errSecAllocate):
self = Allocate
case Int(errSecAuthFailed):
self = AuthFailed
case Int(errSecDecode):
self = Decode
case Int(errSecDuplicateItem):
self = Duplicate
case Int(errSecInteractionNotAllowed):
self = InteractionNotAllowed
case Int(errSecItemNotFound):
self = NotFound
case Int(errSecNotAvailable):
self = NotAvailable
case Int(errSecParam):
self = Param
case Int(errSecUnimplemented):
self = Unimplemented
default:
return nil
}
}
}
// MARK: Locksmith
public class Locksmith: NSObject {
// MARK: Perform request
public class func performRequest(request: LocksmithRequest) throws -> NSDictionary {
let type = request.type
var result: AnyObject?
var status: OSStatus?
let parsedRequest: NSMutableDictionary = parseRequest(request)
let 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)
}
guard let unwrappedStatus = status else {
throw LocksmithError.TypeNotFound
}
let statusCode = Int(unwrappedStatus)
if let error = LocksmithError(fromStatusCode: statusCode) {
throw error
}
var resultsDictionary: NSDictionary?
if result != nil && 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 ?? NSDictionary()
}
// MARK: Private methods
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.
let status: OSStatus = withUnsafeMutablePointer(&result) { SecItemAdd(request, UnsafeMutablePointer($0)) }
return status
}
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)] = request.securityClass.rawValue
if let accessibleMode = request.accessible {
options[String(kSecAttrAccessible)] = accessibleMode.rawValue
}
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
}
}
// MARK: Convenient Class Methods
extension Locksmith {
public class func saveData(data: Dictionary<String, String>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
let saveRequest = LocksmithRequest(userAccount: userAccount, requestType: .Create, data: data, service: service)
try Locksmith.performRequest(saveRequest)
}
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> NSDictionary? {
let readRequest = LocksmithRequest(userAccount: userAccount, service: service)
do {
let dictionary = try Locksmith.performRequest(readRequest)
return dictionary
} catch {
return nil
}
}
public class func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) throws {
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
try Locksmith.performRequest(deleteRequest)
}
public class func updateData(data: Dictionary<String, String>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
let updateRequest = LocksmithRequest(userAccount: userAccount, requestType: .Update, data: data, service: service)
try Locksmith.performRequest(updateRequest)
}
public class func clearKeychain() throws {
// Delete all of the keychain data of the given class
func deleteDataForSecClass(secClass: CFTypeRef) throws {
let request = NSMutableDictionary()
request.setObject(secClass, forKey: String(kSecClass))
let status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
if let status = status {
let statusCode = Int(status)
if let error = LocksmithError(fromStatusCode: statusCode) {
throw error
}
}
}
// For each of the sec class types, delete all of the saved items of that type
let classes = [kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity]
for classType in classes {
do {
try deleteDataForSecClass(classType)
} catch let error as LocksmithError {
// There was an error
// If the error indicates that there was no item with that security class, that's fine.
// Some of the sec classes will have nothing in them in most cases.
if error != LocksmithError.NotFound {
throw LocksmithError.UnableToClear
}
}
}
}
}
// MARK: Dictionary Extension
extension NSMutableDictionary {
func setOptional(optional: AnyObject?, forKey key: NSCopying) {
if let object: AnyObject = optional {
self.setObject(object, forKey: key)
}
}
}
+170 -107
View File
@@ -5,110 +5,122 @@
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
import CoreFoundation
import UIKit
import Security
public let LocksmithDefaultService = NSBundle.mainBundle().infoDictionary![String(kCFBundleIdentifierKey)] as? String ?? "com.locksmith.defaultService"
public let LocksmithErrorDomain = "com.locksmith.error"
public let LocksmithDefaultService = NSBundle.mainBundle().infoDictionary![kCFBundleIdentifierKey] as String
// MARK: Locksmith Error
public enum LocksmithError: String, ErrorType {
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 RequestNotSet = "The request was not set"
case TypeNotFound = "The type was not found"
case UnableToClear = "Unable to clear the keychain"
case Undefined = "An undefined error occurred"
case Unimplemented = "Function or operation not implemented."
init?(fromStatusCode code: Int) {
switch code {
case Int(errSecAllocate):
self = Allocate
case Int(errSecAuthFailed):
self = AuthFailed
case Int(errSecDecode):
self = Decode
case Int(errSecDuplicateItem):
self = Duplicate
case Int(errSecInteractionNotAllowed):
self = InteractionNotAllowed
case Int(errSecItemNotFound):
self = NotFound
case Int(errSecNotAvailable):
self = NotAvailable
case Int(errSecParam):
self = Param
case Int(errSecUnimplemented):
self = Unimplemented
default:
return nil
}
}
}
// MARK: Locksmith
public class Locksmith: NSObject {
// MARK: Perform request
public class func performRequest(request: LocksmithRequest) throws -> NSDictionary? {
class func performRequest(request: LocksmithRequest) -> (NSDictionary?, NSError?) {
let type = request.type
//var result: Unmanaged<AnyObject>? = nil
var result: AnyObject?
var status: OSStatus?
let parsedRequest: NSMutableDictionary = parseRequest(request)
var parsedRequest: NSMutableDictionary = parseRequest(request)
let requestReference = parsedRequest as CFDictionaryRef
var requestReference = parsedRequest as CFDictionaryRef
switch type {
case .Create:
status = SecItemAdd(requestReference, &result)
status = withUnsafeMutablePointer(&result) { SecItemAdd(requestReference, UnsafeMutablePointer($0)) }
case .Read:
status = SecItemCopyMatching(requestReference, &result)
status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(requestReference, UnsafeMutablePointer($0)) }
case .Delete:
status = SecItemDelete(requestReference)
case .Update:
status = Locksmith.performUpdate(requestReference, result: &result)
default:
status = nil
}
guard let unwrappedStatus = status else {
throw LocksmithError.TypeNotFound
}
let statusCode = Int(unwrappedStatus)
if let error = LocksmithError(fromStatusCode: statusCode) {
throw error
}
var resultsDictionary: NSDictionary?
if result != nil && type == .Read && status == errSecSuccess {
if let data = result as? NSData {
// Convert the retrieved data to a dictionary
resultsDictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary
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]))
}
return resultsDictionary
}
// MARK: Private methods
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.
let status: OSStatus = withUnsafeMutablePointer(&result) { SecItemAdd(request, UnsafeMutablePointer($0)) }
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()
@@ -117,11 +129,7 @@ public class Locksmith: NSObject {
options[String(kSecAttrAccessGroup)] = request.group
options[String(kSecAttrService)] = request.service
options[String(kSecAttrSynchronizable)] = request.synchronizable
options[String(kSecClass)] = request.securityClass.rawValue
if let accessibleMode = request.accessible {
options[String(kSecAttrAccessible)] = accessibleMode.rawValue
}
options[String(kSecClass)] = securityCode(request.securityClass)
for (key, option) in options {
parsedRequest.setOptional(option, forKey: key)
@@ -168,75 +176,130 @@ public class Locksmith: NSObject {
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>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
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)
try Locksmith.performRequest(saveRequest)
let (dictionary, error) = Locksmith.performRequest(saveRequest)
return error
}
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> NSDictionary? {
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> (NSDictionary?, NSError?) {
let readRequest = LocksmithRequest(userAccount: userAccount, service: service)
do {
let dictionary = try Locksmith.performRequest(readRequest)
return dictionary
} catch {
return nil
}
return Locksmith.performRequest(readRequest)
}
public class func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) throws {
public class func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> NSError? {
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
try Locksmith.performRequest(deleteRequest)
let (dictionary, error) = Locksmith.performRequest(deleteRequest)
return error
}
public class func updateData(data: Dictionary<String, String>, forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
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)
try Locksmith.performRequest(updateRequest)
let (dictionary, error) = Locksmith.performRequest(updateRequest)
return error
}
public class func clearKeychain() throws {
public class func clearKeychain() -> NSError? {
// Delete all of the keychain data of the given class
func deleteDataForSecClass(secClass: CFTypeRef) throws {
let request = NSMutableDictionary()
func deleteDataForSecClass(secClass: CFTypeRef) -> NSError? {
var request = NSMutableDictionary()
request.setObject(secClass, forKey: String(kSecClass))
let status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
var status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
if let status = status {
let statusCode = Int(status)
if let error = LocksmithError(fromStatusCode: statusCode) {
throw error
}
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]
for classType in classes {
do {
try deleteDataForSecClass(classType)
} catch let error as LocksmithError {
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 security class, that's fine.
// 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.
if error != LocksmithError.NotFound {
throw LocksmithError.UnableToClear
}
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 Extension
// MARK: Dictionary Extensions
extension NSMutableDictionary {
func setOptional(optional: AnyObject?, forKey key: NSCopying) {
if let object: AnyObject = optional {
self.setObject(object, forKey: key)
}
}
}
}
+16 -115
View File
@@ -8,134 +8,35 @@
import UIKit
import Security
// With thanks to http://iosdeveloperzone.com/2014/10/22/taming-foundation-constants-into-swift-enums/
// MARK: Security Class
public enum SecurityClass: RawRepresentable {
public enum SecurityClass: Int {
case GenericPassword, InternetPassword, Certificate, Key, Identity
public init?(rawValue: String) {
switch rawValue {
case String(kSecClassGenericPassword):
self = GenericPassword
case String(kSecClassInternetPassword):
self = InternetPassword
case String(kSecClassCertificate):
self = Certificate
case String(kSecClassKey):
self = Key
case String(kSecClassIdentity):
self = Identity
default:
print("SecurityClass: Invalid raw value provided. Defaulting to .GenericPassword")
self = GenericPassword
}
}
public var rawValue: String {
switch self {
case .GenericPassword:
return String(kSecClassGenericPassword)
case .InternetPassword:
return String(kSecClassInternetPassword)
case .Certificate:
return String(kSecClassCertificate)
case .Key:
return String(kSecClassKey)
case .Identity:
return String(kSecClassIdentity)
}
}
}
// MARK: Accessible
public enum Accessible: RawRepresentable {
case WhenUnlocked, AfterFirstUnlock, Always, WhenUnlockedThisDeviceOnly, AfterFirstUnlockThisDeviceOnly, AlwaysThisDeviceOnly
@available (iOS 8,*)
case WhenPasscodeSetThisDeviceOnly
public init?(rawValue: String) {
switch rawValue {
case String(kSecAttrAccessibleWhenUnlocked):
self = WhenUnlocked
case String(kSecAttrAccessibleAfterFirstUnlock):
self = AfterFirstUnlock
case String(kSecAttrAccessibleAlways):
self = Always
case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly):
self = WhenUnlockedThisDeviceOnly
case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly):
self = AfterFirstUnlockThisDeviceOnly
case String(kSecAttrAccessibleAlwaysThisDeviceOnly):
self = AlwaysThisDeviceOnly
default:
if #available(iOS 8,*) {
if rawValue == String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) {
self = WhenPasscodeSetThisDeviceOnly
} else {
print("Accessible: invalid rawValue provided. Defaulting to Accessible.WhenUnlocked.")
self = WhenUnlocked
}
} else {
print("Accessible: invalid rawValue provided. Defaulting to Accessible.WhenUnlocked.")
self = WhenUnlocked
}
}
}
public var rawValue: String {
switch self {
case .WhenUnlocked:
return String(kSecAttrAccessibleWhenUnlocked)
case .AfterFirstUnlock:
return String(kSecAttrAccessibleAfterFirstUnlock)
case .Always:
return String(kSecAttrAccessibleAlways)
case .WhenPasscodeSetThisDeviceOnly:
if #available(iOS 8.0, *) {
return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
} else {
fatalError("Accessible.WhenPasscodeSetThisDeviceOnly has no raw representation in iOS 7.")
}
case .WhenUnlockedThisDeviceOnly:
return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
case .AfterFirstUnlockThisDeviceOnly:
return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
case .AlwaysThisDeviceOnly:
return String(kSecAttrAccessibleAlwaysThisDeviceOnly)
}
}
}
// MARK: Match Limit
public enum MatchLimit {
public enum MatchLimit: Int {
case One, Many
}
// MARK: Request Type
public enum RequestType {
public enum RequestType: Int {
case Create, Read, Update, Delete
}
// MARK: Locksmith Request
public class LocksmithRequest: NSObject, CustomDebugStringConvertible {
public class LocksmithRequest: NSObject, DebugPrintable {
// Keychain Options
// Required
public var service: String = LocksmithDefaultService
public var userAccount: String
public var type: RequestType = .Read // Default to non-destructive
var service: String = NSBundle.mainBundle().infoDictionary![kCFBundleIdentifierKey] as String // Default to Bundle Identifier
var userAccount: String
var type: RequestType = .Read // Default to non-destructive
// Optional
public var securityClass: SecurityClass = .GenericPassword // Default to password lookup
public var group: String?
public var data: NSDictionary?
public var matchLimit: MatchLimit = .One
public var synchronizable = false
public var accessible: Accessible?
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), userAccount: \(self.userAccount)"
return "service: \(self.service), type: \(self.type.rawValue), userAccount: \(self.userAccount)"
}
required public init(userAccount: String, service: String = LocksmithDefaultService) {
@@ -143,13 +44,13 @@ public class LocksmithRequest: NSObject, CustomDebugStringConvertible {
self.userAccount = userAccount
}
public convenience init(userAccount: String, requestType: RequestType, service: String = LocksmithDefaultService) {
convenience init(userAccount: String, requestType: RequestType, service: String = LocksmithDefaultService) {
self.init(userAccount: userAccount, service: service)
self.type = requestType
}
public convenience init(userAccount: String, requestType: RequestType, data: NSDictionary, service: String = LocksmithDefaultService) {
convenience init(userAccount: String, requestType: RequestType, data: NSDictionary, service: String = LocksmithDefaultService) {
self.init(userAccount: userAccount, requestType: requestType, service: service)
self.data = data
}
}
}
-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.matthewpalmer.${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.1.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+32 -15
View File
@@ -4,20 +4,17 @@ 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)-->
[![Version](https://img.shields.io/cocoapods/v/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![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
Locksmith for Swift 2, simply add the following line to your Podfile:
pod 'Locksmith', :git => 'https://github.com/matthewpalmer/Locksmith.git', :branch => '2.0'
it, simply add the following line to your Podfile:
pod "Locksmith"
### Manual
@@ -30,25 +27,48 @@ In the following examples, you can choose not to provide a value for the `inServ
**Save data**
```swift
try Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount", inService: "myService")
let error = Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")
```
**Save data, specifying a service**
```swift
let error = Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount", inService: "myService")
```
**Load data**
```swift
let dictionary = Locksmith.loadDataForUserAccount("myUserAccount", inService: "myService")
let (dictionary, error) = Locksmith.loadDataForUserAccount("myUserAccount")
```
**Load data, specifying a service**
```swift
let (dictionary, error) = Locksmith.loadDataForUserAccount("myUserAccount", inService: "myService")
```
**Update data**
```swift
try Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount", inService: "myService")
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
try Locksmith.deleteDataForUserAccount("myUserAccount", inService: "myService")
let error = Locksmith.deleteDataForUserAccount("myUserAccount", inService: "myService")
```
## Custom Requests
@@ -60,19 +80,19 @@ To create custom keychain requests, you first have to instantiate a `LocksmithRe
let saveRequest = LocksmithRequest(userAccount: userAccount, data: ["some key": "some value"], service: service)
// Customize the request
saveRequest.synchronizable = true
try Locksmith.performRequest(saveRequest)
Locksmith.performRequest(saveRequest)
```
**Reading**
```swift
let readRequest = LocksmithRequest(userAccount: userAccount, service: service)
let dictionary = try Locksmith.performRequest(readRequest)
let (dictionary, error) = Locksmith.performRequest(readRequest)
```
**Deleting**
```swift
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
try Locksmith.performRequest(deleteRequest)
Locksmith.performRequest(deleteRequest)
```
## LocksmithRequest
@@ -96,9 +116,6 @@ var securityClass: SecurityClass // Defaults to .GenericPassword
var synchronizable: Bool // Defaults to false
```
## Testing
I can't work out why, but opening `Example/Locksmith.xcworkspace` and trying to run the tests from there won't work. (Pull requests greatly appreciated on this!) Instead, you can run the tests by opening `Locksmith.xcodeproj` in the root directory, and doing Product -> Test.
## Author
[Matthew Palmer](http://matthewpalmer.net), matt@matthewpalmer.net