Compare commits

...

42 Commits

Author SHA1 Message Date
Jouni Miettunen e8a716a568 Update pod spec to version 1.2.4 2016-04-14 08:46:28 +03:00
Jouni Miettunen b8fbb91160 Remove CustomDebugStringConvertible for Swift 2.2 2016-04-14 08:43:06 +03:00
Jouni Miettunen 35bc00f6de Swift 2 fixes for test cases 2016-02-09 08:51:53 +02:00
Jouni Miettunen 0e72823fee Update podspec to version 1.2.3 2016-02-09 08:46:48 +02:00
Jouni Miettunen 164dabf456 Swift 2.1 fixes 2016-02-09 08:44:23 +02:00
Matthew Palmer e7ece35421 Update README.md 2015-08-31 21:29:27 +10:00
Matthew Palmer 8dd3200ae3 Update README.md 2015-08-31 21:28:39 +10:00
Matthew Palmer 48f008a26e Update README.md 2015-08-11 22:30:00 +10:00
Matthew Palmer 0dda3a7208 Update README.md
Add note on Swift 2 support
2015-06-23 07:42:03 +10:00
Matthew Palmer c218c1fbea Add note on running tests 2015-05-24 09:34:49 +10:00
Matthew Palmer 68515bdc2c Merge pull request #34 from larslockefeer/CarthageSupport
Fixed some leftover Carthage issues
2015-05-24 09:05:42 +10:00
Lars Lockefeer f38046bb9e Added Info.plist file 2015-05-23 12:50:51 +02:00
Lars Lockefeer e804dd0452 Set deployment target to iOS 8.0 2015-05-22 17:52:26 +02:00
Matthew Palmer 04c6084a8f Merge pull request #33 from larslockefeer/CarthageSupport
Carthage support
2015-05-22 21:05:24 +10:00
Lars Lockefeer fbc549973b Made the xcode scheme shared for Carthage support 2015-05-21 18:30:19 +02:00
Lars Lockefeer 4ad7ca14e6 Made LocksmithDefaultService resilient against absent plist keys 2015-05-21 18:26:06 +02:00
Lars Lockefeer 582f74e305 Updated the tests
The signature of some methods in `Locksmith.swift` was updated, but the
tests weren’t. This commit updates the tests such that they resemble
the API exposed by `Locksmith.swift` again.
2015-05-21 18:25:38 +02:00
Lars Lockefeer 1758691bc2 Restored the project file
The main project file was still based on the Project Structure before
the addition of Cocoapods support. It has now been restored, such that
the project can be opened and compiled
2015-05-21 18:24:03 +02:00
Matthew Palmer 2a21d9d0d1 Merge pull request #30 from matthewpalmer/1.2.1
Update podspec and tests for latest version
2015-04-10 10:18:17 +10:00
matthewpalmer ca70968264 Update podspec and tests for latest version 2015-04-10 10:15:08 +10:00
Matthew Palmer 8017ad0d4e Merge pull request #29 from matthewpalmer/1.2.1
Update README and podspec for 1.2.1
2015-04-10 09:21:46 +10:00
matthewpalmer e77cffad00 Update README and podspec for 1.2.1 2015-04-10 09:18:45 +10:00
matthewpalmer 8d825ab368 Add Swift 1.2 support
Merge branch 'ChaosCoder-master'

* ChaosCoder-master:
  Converted to Swift 1.2
  Trying to fix travis builds
  removing comments
  public'ing all the things
  kSecAttrAccessible
  Update README.md
  Update README.md
  Remove false Travis badge
2015-04-10 08:53:54 +10:00
Andreas Ganske fb4030034d Converted to Swift 1.2 2015-02-11 22:51:53 +01:00
Matthew Palmer 8f21125a77 Trying to fix travis builds 2015-02-04 14:01:02 +11:00
Matthew Palmer 3d6ce4d5f0 Merge pull request #24 from marcelofabri/kSecAttrAccessible
Adding support for kSecAttrAccessible
2015-02-04 13:58:35 +11:00
Marcelo Fabri 4caf3e99b4 removing comments 2015-02-03 23:48:58 -02:00
Marcelo Fabri d04d2fe9b7 public'ing all the things 2015-02-03 23:48:33 -02:00
Marcelo Fabri 440de6f490 kSecAttrAccessible 2015-02-03 23:26:50 -02:00
Matthew Palmer 658233b596 Update README.md 2015-02-03 16:30:22 +11:00
Matthew Palmer 4246e19952 Update README.md 2015-01-28 21:13:33 +11:00
Matthew Palmer b988530bce Remove false Travis badge 2015-01-28 21:12:50 +11:00
Matthew Palmer 73d14afcfa Merge pull request #18 from matthewpalmer/1.2.0
1.2.0
2015-01-28 21:06:45 +11:00
matthewpalmer c8a14a87b0 Attempt to fix travis build 2015-01-28 21:04:18 +11:00
matthewpalmer 73074c7755 Remove old files
Cleanup for release
2015-01-28 21:02:08 +11:00
matthewpalmer 480c51f0bc Update pods to specify 1.2.0 2015-01-28 21:00:31 +11:00
matthewpalmer 780e9afe24 Pre push commit 2015-01-28 20:50:33 +11:00
matthewpalmer a64c1d9dc7 Updated to allow for default services.
Note that this commit comes after a gruelling battle with git and Xcode,
so stuff might get weird
2015-01-28 20:46:17 +11:00
Matthew Palmer 56ae2fb436 Update README.md 2015-01-25 21:59:23 +11:00
Matthew Palmer c8597b8c27 Update README.md 2015-01-25 21:56:04 +11:00
matthewpalmer cf322dcc1a Fixed local podspec version no. and remote url. don't know why this got
messed up
2015-01-25 20:58:54 +11:00
matthewpalmer 19955da055 Prep for cocoapods 2015-01-25 20:46:58 +11:00
37 changed files with 2147 additions and 6747 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: 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
+2
View File
@@ -1,5 +1,7 @@
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 (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
-38
View File
@@ -1,38 +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 = "0.1.0"
s.summary = "A short description of Locksmith."
s.description = <<-DESC
An optional longer description of Locksmith
* Markdown format.
* Don't worry about the indent, we strip it!
DESC
s.homepage = "https://github.com/<GITHUB_USERNAME>/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/<GITHUB_USERNAME>/Locksmith.git", :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.platform = :ios, '7.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
View File
@@ -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": {
+6 -6
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>1.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -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
@@ -0,0 +1 @@
PODS_LOCKSMITH_LOCKSMITH_OTHER_LDFLAGS = -framework "Security" -framework "UIKit"
@@ -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
@@ -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,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/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,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
@@ -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>
@@ -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
@@ -0,0 +1 @@
PODS_TESTS_LOCKSMITH_OTHER_LDFLAGS = -framework "Security" -framework "UIKit"
@@ -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
+39 -22
View File
@@ -9,6 +9,10 @@ import UIKit
import XCTest
import Locksmith
let myService = "myService"
let sampleData = ["key": "value"]
let myUserAccount = "myUserAccount"
class LocksmithTests: XCTestCase {
override func setUp() {
@@ -22,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
View File
@@ -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.
+24 -22
View File
@@ -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>
-19
View File
@@ -1,19 +0,0 @@
//
// Locksmith.h
// Locksmith
//
// Created by Michael Hahn on 12/22/14.
// Copyright (c) 2014 Mathew Palmer. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for Locksmith.
FOUNDATION_EXPORT double LocksmithVersionNumber;
//! Project version string for Locksmith.
FOUNDATION_EXPORT const unsigned char LocksmithVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Locksmith/PublicHeader.h>
-303
View File
@@ -1,303 +0,0 @@
//
// Locksmith.swift
// Locksmith-Demo
//
// Created by Matthew Palmer on 26/10/2014.
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
import UIKit
public let LocksmithErrorDomain = "com.locksmith.error"
public class Locksmith: NSObject {
// MARK: Perform request
class func performRequest(request: LocksmithRequest) -> (NSDictionary?, NSError?) {
let type = request.type
//var result: Unmanaged<AnyObject>? = nil
var result: AnyObject?
var status: OSStatus?
var parsedRequest: NSMutableDictionary = parseRequest(request)
var requestReference = parsedRequest as CFDictionaryRef
switch type {
case .Create:
status = withUnsafeMutablePointer(&result) { SecItemAdd(requestReference, UnsafeMutablePointer($0)) }
case .Read:
status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(requestReference, UnsafeMutablePointer($0)) }
case .Delete:
status = SecItemDelete(requestReference)
case .Update:
status = Locksmith.performUpdate(requestReference, result: &result)
default:
status = nil
}
if let status = status {
var statusCode = Int(status)
let error = Locksmith.keychainError(forCode: statusCode)
var resultsDictionary: NSDictionary?
if result != nil {
if type == .Read && status == errSecSuccess {
if let data = result as? NSData {
// Convert the retrieved data to a dictionary
resultsDictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary
}
}
}
return (resultsDictionary, error)
} else {
let code = LocksmithErrorCode.TypeNotFound.rawValue
let message = internalErrorMessage(forCode: code)
return (nil, NSError(domain: LocksmithErrorDomain, code: code, userInfo: ["message": message]))
}
}
private class func performUpdate(request: CFDictionaryRef, inout result: AnyObject?) -> OSStatus {
// We perform updates to the keychain by first deleting the matching object, then writing to it with the new value.
SecItemDelete(request)
// Even if the delete request failed (e.g. if the item didn't exist before), still try to save the new item.
// If we get an error saving, we'll tell the user about it.
var status: OSStatus = withUnsafeMutablePointer(&result) { SecItemAdd(request, UnsafeMutablePointer($0)) }
return status
}
// MARK: Error Lookup
enum ErrorMessage: String {
case Allocate = "Failed to allocate memory."
case AuthFailed = "Authorization/Authentication failed."
case Decode = "Unable to decode the provided data."
case Duplicate = "The item already exists."
case InteractionNotAllowed = "Interaction with the Security Server is not allowed."
case NoError = "No error."
case NotAvailable = "No trust results are available."
case NotFound = "The item cannot be found."
case Param = "One or more parameters passed to the function were not valid."
case Unimplemented = "Function or operation not implemented."
}
enum LocksmithErrorCode: Int {
case RequestNotSet = 1
case TypeNotFound = 2
case UnableToClear = 3
}
enum LocksmithErrorMessage: String {
case RequestNotSet = "keychainRequest was not set."
case TypeNotFound = "The type of request given was undefined."
case UnableToClear = "Unable to clear the keychain"
}
class func keychainError(forCode statusCode: Int) -> NSError? {
var error: NSError?
if statusCode != Int(errSecSuccess) {
let message = errorMessage(statusCode)
// println("Keychain request failed. Code: \(statusCode). Message: \(message)")
error = NSError(domain: LocksmithErrorDomain, code: statusCode, userInfo: ["message": message])
}
return error
}
// MARK: Private methods
private class func internalErrorMessage(forCode statusCode: Int) -> NSString {
switch statusCode {
case LocksmithErrorCode.RequestNotSet.rawValue:
return LocksmithErrorMessage.RequestNotSet.rawValue
case LocksmithErrorCode.UnableToClear.rawValue:
return LocksmithErrorMessage.UnableToClear.rawValue
default:
return "Error message for code \(statusCode) not set"
}
}
private class func parseRequest(request: LocksmithRequest) -> NSMutableDictionary {
var parsedRequest = NSMutableDictionary()
var options = [String: AnyObject?]()
options[String(kSecAttrAccount)] = request.userAccount
options[String(kSecAttrAccessGroup)] = request.group
options[String(kSecAttrService)] = request.service
options[String(kSecAttrSynchronizable)] = request.synchronizable
options[String(kSecClass)] = securityCode(request.securityClass)
for (key, option) in options {
parsedRequest.setOptional(option, forKey: key)
}
switch request.type {
case .Create:
parsedRequest = parseCreateRequest(request, inDictionary: parsedRequest)
case .Delete:
parsedRequest = parseDeleteRequest(request, inDictionary: parsedRequest)
case .Update:
parsedRequest = parseCreateRequest(request, inDictionary: parsedRequest)
default: // case .Read:
parsedRequest = parseReadRequest(request, inDictionary: parsedRequest)
}
return parsedRequest
}
private class func parseCreateRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
if let data = request.data {
let encodedData = NSKeyedArchiver.archivedDataWithRootObject(data)
dictionary.setObject(encodedData, forKey: String(kSecValueData))
}
return dictionary
}
private class func parseReadRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
dictionary.setOptional(kCFBooleanTrue, forKey: String(kSecReturnData))
switch request.matchLimit {
case .One:
dictionary.setObject(kSecMatchLimitOne, forKey: String(kSecMatchLimit))
case .Many:
dictionary.setObject(kSecMatchLimitAll, forKey: String(kSecMatchLimit))
}
return dictionary
}
private class func parseDeleteRequest(request: LocksmithRequest, inDictionary dictionary: NSMutableDictionary) -> NSMutableDictionary {
return dictionary
}
private class func errorMessage(code: Int) -> NSString {
switch code {
case Int(errSecAllocate):
return ErrorMessage.Allocate.rawValue
case Int(errSecAuthFailed):
return ErrorMessage.AuthFailed.rawValue
case Int(errSecDecode):
return ErrorMessage.Decode.rawValue
case Int(errSecDuplicateItem):
return ErrorMessage.Duplicate.rawValue
case Int(errSecInteractionNotAllowed):
return ErrorMessage.InteractionNotAllowed.rawValue
case Int(errSecItemNotFound):
return ErrorMessage.NotFound.rawValue
case Int(errSecNotAvailable):
return ErrorMessage.NotAvailable.rawValue
case Int(errSecParam):
return ErrorMessage.Param.rawValue
case Int(errSecSuccess):
return ErrorMessage.NoError.rawValue
case Int(errSecUnimplemented):
return ErrorMessage.Unimplemented.rawValue
default:
return "Undocumented error with code \(code)."
}
}
private class func securityCode(securityClass: SecurityClass) -> CFStringRef {
switch securityClass {
case .GenericPassword:
return kSecClassGenericPassword
case .Certificate:
return kSecClassCertificate
case .Identity:
return kSecClassIdentity
case .InternetPassword:
return kSecClassInternetPassword
case .Key:
return kSecClassKey
default:
return kSecClassGenericPassword
}
}
}
// MARK: Convenient Class Methods
extension Locksmith {
public class func saveData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Create, data: data)
let (dictionary, error) = Locksmith.performRequest(saveRequest)
return error
}
public class func loadDataInService(service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
let readRequest = LocksmithRequest(service: service, userAccount: userAccount)
return Locksmith.performRequest(readRequest)
}
public class func deleteDataInService(service: String, forUserAccount userAccount: String) -> NSError? {
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Delete)
let (dictionary, error) = Locksmith.performRequest(deleteRequest)
return error
}
public class func updateData(data: Dictionary<String, String>, inService service: String, forUserAccount userAccount: String) -> NSError? {
let updateRequest = LocksmithRequest(service: service, userAccount: userAccount, requestType: .Update, data: data)
let (dictionary, error) = Locksmith.performRequest(updateRequest)
return error
}
public class func clearKeychain() -> NSError? {
// Delete all of the keychain data of the given class
func deleteDataForSecClass(secClass: CFTypeRef) -> NSError? {
var request = NSMutableDictionary()
request.setObject(secClass, forKey: String(kSecClass))
var status: OSStatus? = SecItemDelete(request as CFDictionaryRef)
if let status = status {
var statusCode = Int(status)
return Locksmith.keychainError(forCode: statusCode)
}
return nil
}
// For each of the sec class types, delete all of the saved items of that type
let classes = [kSecClassGenericPassword, kSecClassInternetPassword, kSecClassCertificate, kSecClassKey, kSecClassIdentity]
let errors: [NSError?] = classes.map({
return deleteDataForSecClass($0)
})
// Remove those that were successful, or failed with an acceptable error code
let filtered = errors.filter({
if let error = $0 {
// There was an error
// If the error indicates that there was no item with that sec class, that's fine.
// Some of the sec classes will have nothing in them in most cases.
return error.code != Int(errSecItemNotFound) ? true : false
}
// There was no error
return false
})
// If the filtered array is empty, then everything went OK
if filtered.isEmpty {
return nil
}
// At least one of the delete operations failed
let code = LocksmithErrorCode.UnableToClear.rawValue
let message = internalErrorMessage(forCode: code)
return NSError(domain: LocksmithErrorDomain, code: code, userInfo: ["message": message])
}
}
// MARK: Dictionary Extensions
extension NSMutableDictionary {
func setOptional(optional: AnyObject?, forKey key: NSCopying) {
if let object: AnyObject = optional {
self.setObject(object, forKey: key)
}
}
}
-56
View File
@@ -1,56 +0,0 @@
//
// LocksmithRequest.swift
// Locksmith-Demo
//
// Created by Matthew Palmer on 26/10/2014.
// Copyright (c) 2014 Colour Coding. All rights reserved.
//
import UIKit
public enum SecurityClass: Int {
case GenericPassword, InternetPassword, Certificate, Key, Identity
}
public enum MatchLimit: Int {
case One, Many
}
public enum RequestType: Int {
case Create, Read, Update, Delete
}
public class LocksmithRequest: NSObject, DebugPrintable {
// Keychain Options
// Required
var service: String
var userAccount: String
var type: RequestType = .Read // Default to non-destructive
// Optional
var securityClass: SecurityClass = .GenericPassword // Default to password lookup
var group: String?
var data: NSDictionary?
var matchLimit: MatchLimit = .One
var synchronizable = false
// Debugging
override public var debugDescription: String {
return "service: \(self.service), type: \(self.type.rawValue), userAccount: \(self.userAccount)"
}
required public init(service: String, userAccount: String) {
self.service = service
self.userAccount = userAccount
}
convenience init(service: String, userAccount: String, requestType: RequestType) {
self.init(service: service, userAccount: userAccount)
self.type = requestType
}
convenience init(service: String, userAccount: String, requestType: RequestType, data: NSDictionary) {
self.init(service: service, userAccount: userAccount, requestType: requestType)
self.data = data
}
}
+25 -24
View File
@@ -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
View File
@@ -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)
}
}
}
}
+21 -15
View File
@@ -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>
+58 -15
View File
@@ -1,43 +1,82 @@
> This is Locksmiths compatibility branch for Swift 1.2
# Locksmith
A sane way to work with the iOS Keychain in Swift.
[![CI Status](http://img.shields.io/travis/matthewpalmer/Locksmith.svg?style=flat)](https://travis-ci.org/matthewpalmer/Locksmith)
<!--[![CI Status](http://img.shields.io/travis/matthewpalmer/Locksmith.svg?style=flat)](https://travis-ci.org/matthewpalmer/Locksmith)-->
[![Version](https://img.shields.io/cocoapods/v/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
[![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
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,10 +124,13 @@ 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
matthewpalmer, matt@matthewpalmer.net
[Matthew Palmer](http://matthewpalmer.net), matt@matthewpalmer.net
## License
Locksmith is available under the MIT license. See the LICENSE file for more info.
Locksmith is available under the MIT license. See the LICENSE file for more info.