Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8a716a568 | |||
| b8fbb91160 | |||
| 35bc00f6de | |||
| 0e72823fee | |||
| 164dabf456 | |||
| e7ece35421 | |||
| 8dd3200ae3 | |||
| 48f008a26e | |||
| 0dda3a7208 | |||
| c218c1fbea | |||
| 68515bdc2c | |||
| f38046bb9e | |||
| e804dd0452 | |||
| 04c6084a8f | |||
| fbc549973b | |||
| 4ad7ca14e6 | |||
| 582f74e305 | |||
| 1758691bc2 | |||
| 2a21d9d0d1 | |||
| ca70968264 | |||
| 8017ad0d4e | |||
| e77cffad00 | |||
| 8d825ab368 | |||
| fb4030034d | |||
| 8f21125a77 | |||
| 3d6ce4d5f0 | |||
| 4caf3e99b4 | |||
| d04d2fe9b7 | |||
| 440de6f490 | |||
| 658233b596 | |||
| 4246e19952 | |||
| b988530bce | |||
| 73d14afcfa | |||
| c8a14a87b0 | |||
| 73074c7755 | |||
| 480c51f0bc | |||
| 780e9afe24 | |||
| a64c1d9dc7 |
+2
-2
@@ -2,7 +2,7 @@
|
||||
# * http://www.objc.io/issue-6/travis-ci.html
|
||||
# * https://github.com/supermarin/xcpretty#usage
|
||||
|
||||
language: objective-c
|
||||
language: swift
|
||||
# cache: cocoapods
|
||||
# podfile: Example/Podfile
|
||||
# before_install:
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
|
||||
use_frameworks!
|
||||
|
||||
platform :ios, '8.0'
|
||||
|
||||
target 'Locksmith', :exclusive => true do
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
PODS:
|
||||
- Expecta (0.3.1)
|
||||
- Locksmith (0.1.0)
|
||||
- Locksmith (1.2.1)
|
||||
- Specta (0.2.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -10,11 +10,11 @@ DEPENDENCIES:
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Locksmith:
|
||||
:path: ../
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
|
||||
Locksmith: b9371e929ae783d9ee80af42278258e9cedda7f6
|
||||
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
|
||||
Expecta: a354d4633409dd9fe8c4f5ff5130426adbe31628
|
||||
Locksmith: 770f6c5c6e5c5c712d1f00e709c62b8a7240c716
|
||||
Specta: 15a276a6343867b426d5ed135d5aa4d04123a573
|
||||
|
||||
COCOAPODS: 0.36.0.beta.1
|
||||
COCOAPODS: 0.36.3
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
#
|
||||
# Be sure to run `pod lib lint Locksmith.podspec' to ensure this is a
|
||||
# valid spec and remove all comments before submitting the spec.
|
||||
#
|
||||
# Any lines starting with a # are optional, but encouraged
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
|
||||
#
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Locksmith"
|
||||
s.version = "1.1.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.
|
||||
It provides a fast and intuitive way to work with the C Keychain API.
|
||||
Results are provided as tuples, and errors are informative and easily detected.
|
||||
DESC
|
||||
s.homepage = "https://github.com/matthewpalmer/Locksmith"
|
||||
# s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
|
||||
s.license = 'MIT'
|
||||
s.author = { "matthewpalmer" => "matt@matthewpalmer.net" }
|
||||
s.source = { :git => "https://github.com/matthewpalmer/Locksmith.git", :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/_matthewpalmer'
|
||||
|
||||
s.platform = :ios, '8.0'
|
||||
s.requires_arc = true
|
||||
|
||||
s.source_files = 'Pod/Classes'
|
||||
s.resource_bundles = {
|
||||
'Locksmith' => ['Pod/Assets/*.png']
|
||||
}
|
||||
|
||||
# s.public_header_files = 'Pod/Classes/**/*.h'
|
||||
# s.frameworks = 'UIKit', 'MapKit'
|
||||
# s.dependency 'AFNetworking', '~> 2.3'
|
||||
end
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Locksmith",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.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.1.0"
|
||||
"tag": "1.2.1"
|
||||
},
|
||||
"social_media_url": "https://twitter.com/_matthewpalmer",
|
||||
"platforms": {
|
||||
|
||||
Generated
+6
-6
@@ -1,6 +1,6 @@
|
||||
PODS:
|
||||
- Expecta (0.3.1)
|
||||
- Locksmith (0.1.0)
|
||||
- Locksmith (1.2.1)
|
||||
- Specta (0.2.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -10,11 +10,11 @@ DEPENDENCIES:
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Locksmith:
|
||||
:path: ../
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Expecta: 03aabd0a89d8dea843baecb19a7fd7466a69a31d
|
||||
Locksmith: b9371e929ae783d9ee80af42278258e9cedda7f6
|
||||
Specta: 9141310f46b1f68b676650ff2854e1ed0b74163a
|
||||
Expecta: a354d4633409dd9fe8c4f5ff5130426adbe31628
|
||||
Locksmith: 770f6c5c6e5c5c712d1f00e709c62b8a7240c716
|
||||
Specta: 15a276a6343867b426d5ed135d5aa4d04123a573
|
||||
|
||||
COCOAPODS: 0.36.0.beta.1
|
||||
COCOAPODS: 0.36.3
|
||||
|
||||
+1746
-6151
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.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
Example/Pods/Target Support Files/Pods-Locksmith-Locksmith/Pods-Locksmith-Locksmith-Private.xcconfig
Generated
+4
-3
@@ -3,7 +3,8 @@ CONFIGURATION_BUILD_DIR = $PODS_FRAMEWORK_BUILD_PATH
|
||||
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 = -ObjC
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_LDFLAGS = ${PODS_LOCKSMITH_LOCKSMITH_OTHER_LDFLAGS} -ObjC
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
SKIP_INSTALL = YES
|
||||
Generated
+1
@@ -0,0 +1 @@
|
||||
PODS_LOCKSMITH_LOCKSMITH_OTHER_LDFLAGS = -framework "Security" -framework "UIKit"
|
||||
+3
-3
@@ -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_PATCH_Locksmith 0
|
||||
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
|
||||
#define COCOAPODS_VERSION_MINOR_Locksmith 2
|
||||
#define COCOAPODS_VERSION_PATCH_Locksmith 1
|
||||
|
||||
|
||||
+7
-4
@@ -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,10 +28,13 @@ 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 @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 --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
|
||||
for lib in $swift_runtime_libs; do
|
||||
echo "rsync -av \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
|
||||
rsync -av "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
|
||||
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
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -6,6 +6,8 @@ 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
|
||||
@@ -36,6 +38,7 @@ 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"
|
||||
@@ -54,7 +57,7 @@ if [[ "${ACTION}" == "install" ]]; then
|
||||
fi
|
||||
rm -f "$RESOURCES_TO_COPY"
|
||||
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ]
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
|
||||
then
|
||||
case "${TARGETED_DEVICE_FAMILY}" in
|
||||
1,2)
|
||||
@@ -70,5 +73,6 @@ then
|
||||
TARGET_DEVICE_ARGS="--target-device mac"
|
||||
;;
|
||||
esac
|
||||
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}"
|
||||
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}"
|
||||
fi
|
||||
|
||||
+2
-2
@@ -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 = -ObjC -framework "Locksmith"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Locksmith"
|
||||
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
+2
-2
@@ -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 = -ObjC -framework "Locksmith"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Locksmith"
|
||||
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Locksmith
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
+2
-1
@@ -5,4 +5,5 @@ 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}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
SKIP_INSTALL = YES
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<string>1.2.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
Generated
+4
-3
@@ -3,7 +3,8 @@ CONFIGURATION_BUILD_DIR = $PODS_FRAMEWORK_BUILD_PATH
|
||||
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 = -ObjC
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_LDFLAGS = ${PODS_TESTS_LOCKSMITH_OTHER_LDFLAGS} -ObjC
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
SKIP_INSTALL = YES
|
||||
+1
@@ -0,0 +1 @@
|
||||
PODS_TESTS_LOCKSMITH_OTHER_LDFLAGS = -framework "Security" -framework "UIKit"
|
||||
+2
-1
@@ -5,4 +5,5 @@ 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}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
SKIP_INSTALL = YES
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
// Locksmith
|
||||
#define COCOAPODS_POD_AVAILABLE_Locksmith
|
||||
#define COCOAPODS_VERSION_MAJOR_Locksmith 0
|
||||
#define COCOAPODS_VERSION_MINOR_Locksmith 1
|
||||
#define COCOAPODS_VERSION_PATCH_Locksmith 0
|
||||
#define COCOAPODS_VERSION_MAJOR_Locksmith 1
|
||||
#define COCOAPODS_VERSION_MINOR_Locksmith 2
|
||||
#define COCOAPODS_VERSION_PATCH_Locksmith 1
|
||||
|
||||
// Specta
|
||||
#define COCOAPODS_POD_AVAILABLE_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,10 +28,13 @@ 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 @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 --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
|
||||
for lib in $swift_runtime_libs; do
|
||||
echo "rsync -av \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
|
||||
rsync -av "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
|
||||
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
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ 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
|
||||
@@ -36,6 +38,7 @@ 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"
|
||||
@@ -54,7 +57,7 @@ if [[ "${ACTION}" == "install" ]]; then
|
||||
fi
|
||||
rm -f "$RESOURCES_TO_COPY"
|
||||
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ]
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
|
||||
then
|
||||
case "${TARGETED_DEVICE_FAMILY}" in
|
||||
1,2)
|
||||
@@ -70,5 +73,6 @@ then
|
||||
TARGET_DEVICE_ARGS="--target-device mac"
|
||||
;;
|
||||
esac
|
||||
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}"
|
||||
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}"
|
||||
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 = -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
|
||||
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-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 = -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -framework "Expecta" -framework "Locksmith" -framework "Specta"
|
||||
OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS)
|
||||
OTHER_SWIFT_FLAGS = "-D COCOAPODS"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-Tests
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
@@ -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,67 +26,80 @@ 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")
|
||||
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading one item")
|
||||
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")
|
||||
XCTAssert(dictionary3!.valueForKey("word")! as NSString == "definition" && error == nil, "❌: loading multiple items")
|
||||
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()
|
||||
|
||||
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")
|
||||
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
@@ -9,7 +9,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Locksmith"
|
||||
s.version = "1.1.0"
|
||||
s.version = "1.2.4"
|
||||
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.
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
BFFB19D61A4870A300CCFFC3 /* Locksmith.h in Headers */ = {isa = PBXBuildFile; fileRef = BFFB19D51A4870A300CCFFC3 /* Locksmith.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
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 */; };
|
||||
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 */
|
||||
@@ -25,14 +26,13 @@
|
||||
/* 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,9 +75,8 @@
|
||||
BFFB19D21A4870A300CCFFC3 /* Locksmith */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFFB19EC1A4870E400CCFFC3 /* LocksmithRequest.swift */,
|
||||
BFFB19ED1A4870E400CCFFC3 /* Locksmith.swift */,
|
||||
BFFB19D51A4870A300CCFFC3 /* Locksmith.h */,
|
||||
9DF0A2BE1B0E394F0049F83A /* Locksmith.swift */,
|
||||
9DF0A2BF1B0E394F0049F83A /* LocksmithRequest.swift */,
|
||||
BFFB19D31A4870A300CCFFC3 /* Supporting Files */,
|
||||
);
|
||||
path = Locksmith;
|
||||
@@ -86,7 +85,7 @@
|
||||
BFFB19D31A4870A300CCFFC3 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFFB19D41A4870A300CCFFC3 /* Info.plist */,
|
||||
9D60D8131B10927B00BE14A9 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
@@ -103,7 +102,7 @@
|
||||
BFFB19E01A4870A300CCFFC3 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFFB19E11A4870A300CCFFC3 /* Info.plist */,
|
||||
9DF0A2C71B0E3D370049F83A /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
@@ -115,7 +114,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFFB19D61A4870A300CCFFC3 /* Locksmith.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -198,6 +196,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9D60D8141B10927B00BE14A9 /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -205,6 +204,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9DF0A2C81B0E3D370049F83A /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -215,8 +215,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFFB19EF1A4870E400CCFFC3 /* Locksmith.swift in Sources */,
|
||||
BFFB19EE1A4870E400CCFFC3 /* LocksmithRequest.swift in Sources */,
|
||||
9DF0A2C11B0E394F0049F83A /* LocksmithRequest.swift in Sources */,
|
||||
9DF0A2C01B0E394F0049F83A /* Locksmith.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -274,7 +274,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -314,7 +314,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -327,11 +327,12 @@
|
||||
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";
|
||||
INFOPLIST_FILE = Locksmith/Info.plist;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Pod/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -343,11 +344,12 @@
|
||||
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";
|
||||
INFOPLIST_FILE = Locksmith/Info.plist;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Pod/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -367,7 +369,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)";
|
||||
};
|
||||
@@ -380,7 +382,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)";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -22,68 +22,69 @@ class LocksmithTests: XCTestCase {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
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(["key": "value"], forUserAccount: "user", inService: "myService")
|
||||
|
||||
// Should fail
|
||||
let error2 = Locksmith.saveData(["key": "value"], inService: "myService", forUserAccount: "user")
|
||||
let error2 = Locksmith.saveData(["key": "value"], forUserAccount: "user", inService: "myService")
|
||||
|
||||
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() {
|
||||
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")
|
||||
XCTAssert(dictionary!.valueForKey("key")! as NSString == "value" && error == nil, "❌: loading one item")
|
||||
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, _) = Locksmith.loadDataForUserAccount("user2", inService: "myService")
|
||||
let (dictionary3, _) = 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")
|
||||
XCTAssert(dictionary3!.valueForKey("word")! as NSString == "definition" && error == nil, "❌: loading multiple items")
|
||||
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()
|
||||
|
||||
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")
|
||||
let error = Locksmith.updateData(["key": "newvalue"], forUserAccount: "user1", inService: "myService")
|
||||
let (dictionary, _) = 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")
|
||||
}
|
||||
|
||||
@@ -91,10 +92,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")
|
||||
}
|
||||
|
||||
@@ -105,4 +106,4 @@ class LocksmithTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+44
-24
@@ -9,18 +9,20 @@ import UIKit
|
||||
import Security
|
||||
|
||||
public let LocksmithErrorDomain = "com.locksmith.error"
|
||||
public let LocksmithDefaultService = NSBundle.mainBundle().bundleIdentifier ?? "com.locksmith.defaultService"
|
||||
|
||||
|
||||
public class Locksmith: NSObject {
|
||||
// MARK: Perform request
|
||||
class func performRequest(request: LocksmithRequest) -> (NSDictionary?, NSError?) {
|
||||
public 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)
|
||||
let parsedRequest: NSMutableDictionary = parseRequest(request)
|
||||
|
||||
var requestReference = parsedRequest as CFDictionaryRef
|
||||
let requestReference = parsedRequest as CFDictionaryRef
|
||||
|
||||
switch type {
|
||||
case .Create:
|
||||
@@ -31,12 +33,10 @@ public class Locksmith: NSObject {
|
||||
status = SecItemDelete(requestReference)
|
||||
case .Update:
|
||||
status = Locksmith.performUpdate(requestReference, result: &result)
|
||||
default:
|
||||
status = nil
|
||||
}
|
||||
|
||||
if let status = status {
|
||||
var statusCode = Int(status)
|
||||
let statusCode = Int(status)
|
||||
let error = Locksmith.keychainError(forCode: statusCode)
|
||||
var resultsDictionary: NSDictionary?
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Locksmith: NSObject {
|
||||
// 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)) }
|
||||
let status: OSStatus = withUnsafeMutablePointer(&result) { SecItemAdd(request, UnsafeMutablePointer($0)) }
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -128,6 +128,9 @@ public class Locksmith: NSObject {
|
||||
options[String(kSecAttrService)] = request.service
|
||||
options[String(kSecAttrSynchronizable)] = request.synchronizable
|
||||
options[String(kSecClass)] = securityCode(request.securityClass)
|
||||
if let accessibleMode = request.accessible {
|
||||
options[String(kSecAttrAccessible)] = accessible(accessibleMode)
|
||||
}
|
||||
|
||||
for (key, option) in options {
|
||||
parsedRequest.setOptional(option, forKey: key)
|
||||
@@ -214,47 +217,64 @@ public class Locksmith: NSObject {
|
||||
return kSecClassInternetPassword
|
||||
case .Key:
|
||||
return kSecClassKey
|
||||
default:
|
||||
return kSecClassGenericPassword
|
||||
}
|
||||
}
|
||||
|
||||
private class func accessible(accessible: Accessible) -> CFStringRef {
|
||||
switch accessible {
|
||||
case .WhenUnlock:
|
||||
return kSecAttrAccessibleWhenUnlocked
|
||||
case .AfterFirstUnlock:
|
||||
return kSecAttrAccessibleAfterFirstUnlock
|
||||
case .Always:
|
||||
return kSecAttrAccessibleAlways
|
||||
case .WhenPasscodeSetThisDeviceOnly:
|
||||
return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
|
||||
case .WhenUnlockedThisDeviceOnly:
|
||||
return kSecAttrAccessibleWhenUnlockedThisDeviceOnly
|
||||
case .AfterFirstUnlockThisDeviceOnly:
|
||||
return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
||||
case .AlwaysThisDeviceOnly:
|
||||
return kSecAttrAccessibleAlwaysThisDeviceOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
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 (_, 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)
|
||||
let (dictionary, error) = Locksmith.performRequest(deleteRequest)
|
||||
public class func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> NSError? {
|
||||
let deleteRequest = LocksmithRequest(userAccount: userAccount, requestType: .Delete, service: service)
|
||||
let (_, 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)
|
||||
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 (_, 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()
|
||||
let request = NSMutableDictionary()
|
||||
request.setObject(secClass, forKey: String(kSecClass))
|
||||
|
||||
var status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
|
||||
let status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
|
||||
|
||||
if let status = status {
|
||||
var statusCode = Int(status)
|
||||
let statusCode = Int(status)
|
||||
return Locksmith.keychainError(forCode: statusCode)
|
||||
}
|
||||
|
||||
@@ -300,4 +320,4 @@ extension NSMutableDictionary {
|
||||
self.setObject(object, forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,37 +20,43 @@ public enum RequestType: Int {
|
||||
case Create, Read, Update, Delete
|
||||
}
|
||||
|
||||
public class LocksmithRequest: NSObject, DebugPrintable {
|
||||
public enum Accessible: Int {
|
||||
case WhenUnlock, AfterFirstUnlock, Always, WhenPasscodeSetThisDeviceOnly,
|
||||
WhenUnlockedThisDeviceOnly, AfterFirstUnlockThisDeviceOnly, AlwaysThisDeviceOnly
|
||||
}
|
||||
|
||||
public class LocksmithRequest: NSObject {
|
||||
// Keychain Options
|
||||
// Required
|
||||
var service: String
|
||||
var userAccount: String
|
||||
var type: RequestType = .Read // Default to non-destructive
|
||||
public var service: String = LocksmithDefaultService
|
||||
public var userAccount: String
|
||||
public 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
|
||||
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?
|
||||
|
||||
// Debugging
|
||||
override public var debugDescription: String {
|
||||
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)
|
||||
public 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)
|
||||
public convenience init(userAccount: String, requestType: RequestType, data: NSDictionary, service: String = LocksmithDefaultService) {
|
||||
self.init(userAccount: userAccount, requestType: requestType, service: service)
|
||||
self.data = data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.locksmith.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>com.matthewpalmer.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>1.1.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
@@ -1,43 +1,82 @@
|
||||
> This is Locksmith’s compatibility branch for Swift 1.2
|
||||
|
||||
# Locksmith
|
||||
|
||||
A sane way to work with the iOS Keychain in Swift.
|
||||
|
||||
<!--[](https://travis-ci.org/matthewpalmer/Locksmith)-->
|
||||
[](http://cocoadocs.org/docsets/Locksmith)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/Locksmith)
|
||||
[](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", :git => 'https://github.com/matthewpalmer/Locksmith.git', :branch => '1.2.2'
|
||||
|
||||
### 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**
|
||||
|
||||
- writes the data to the keychain if it does not exist already
|
||||
|
||||
```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**
|
||||
|
||||
- overwrites whatever is stored on the keychain under this user account (if nothing is stored, we save as normal)
|
||||
|
||||
```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 +84,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 +93,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)
|
||||
```
|
||||
|
||||
@@ -84,6 +124,9 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user