Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2eec09637f | |||
| 477977a11c | |||
| 1830acd0ee | |||
| 0f06f2d911 | |||
| 3910c07e5b | |||
| e79e4aeb4e | |||
| f2b44e6a1c | |||
| 100fe4c13c | |||
| 562a68f2bd | |||
| 373edfae08 | |||
| 31d6fc2ef7 | |||
| 76cfd3c3e4 | |||
| 2ebf806a1a | |||
| 22bcd61cff | |||
| f8e007ebda | |||
| 267fe346c9 | |||
| b4ebf6f318 | |||
| 8496e635c1 | |||
| 17579e4417 | |||
| c0906fbda3 | |||
| b61c058597 | |||
| 323cae20ec | |||
| e99470b94a | |||
| 1aae774695 | |||
| 7bf3ad867f | |||
| 002ce89a89 | |||
| ef4a62f475 | |||
| d09f7af9d7 | |||
| 9e99695143 | |||
| 27fbf4e935 | |||
| 7a6f2a6724 | |||
| 1bca5f35fb | |||
| 1e3b72b0b6 | |||
| 41b6644b18 | |||
| a449ebf527 | |||
| ccda8fb901 | |||
| f94eff2115 | |||
| ee96b18cfd | |||
| 79cf22216f | |||
| 4bfbf86f73 | |||
| f0c4572ff4 | |||
| af441ab648 | |||
| 6074c2640a | |||
| 351b0a4c82 | |||
| 38b29df3ca | |||
| 9efa16d266 | |||
| 709d323463 | |||
| e06271dd90 | |||
| 1b40db9405 | |||
| afe4706789 | |||
| a89f2584ce | |||
| cb920c0318 | |||
| b11dea36b4 | |||
| 07a9234d3e | |||
| b193e300a5 | |||
| 7e53fc8322 | |||
| 9ce641f262 | |||
| 17d52574ab | |||
| 5602015a6d | |||
| 737c262e8b | |||
| 8dc3ef57c6 | |||
| 48f008a26e | |||
| 7a6395f189 | |||
| c48ce652bd | |||
| 716759b767 | |||
| 3da0e97cc6 | |||
| 03b5015f4f | |||
| 1b0c092708 | |||
| 25917eaf15 | |||
| 0dda3a7208 | |||
| 4dce464fb0 | |||
| 5e508a7551 | |||
| a2e58ce87f | |||
| 825d7d2485 | |||
| 0418e37185 | |||
| 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 | |||
| 56ae2fb436 | |||
| c8597b8c27 | |||
| cf322dcc1a | |||
| 19955da055 | |||
| d172aac89a | |||
| b6b4f48b60 | |||
| 0b5e1c085d | |||
| fc6d06bc80 | |||
| bdc30e42d4 | |||
| e1cae42bf0 | |||
| 615cf5d375 | |||
| 45795fc90b | |||
| 364679a64f | |||
| a8d082ca0a | |||
| 7e9a292ace | |||
| 6d6480b5eb | |||
| df7724944b | |||
| f823d2f166 | |||
| acd5f89a62 | |||
| d2bde3b36e | |||
| 130773ac4d | |||
| 9d6e0e420f | |||
| 90b2c815f3 | |||
| bf43e70035 | |||
| 213a6231b8 | |||
| c06d1fbcfb | |||
| a03582b242 | |||
| f9b125c00e | |||
| 17ede022a5 | |||
| 883b340665 | |||
| dd4e8be44d | |||
| 9e20d2f4ec | |||
| 27a7e90721 | |||
| d07cbdc7a1 | |||
| dde2b99101 | |||
| 50e2cc5523 | |||
| 475d29c147 | |||
| 745158bf37 | |||
| 6bec28e642 | |||
| acf0df75d0 | |||
| fb0be8af55 | |||
| 1f2a865abe | |||
| 075fd7800c | |||
| e1b538eed9 | |||
| d788b76f90 |
+26
-11
@@ -1,16 +1,31 @@
|
||||
<<<<<<< HEAD
|
||||
# OS X
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
=======
|
||||
# Xcode
|
||||
.DS_Store
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
*.xcworkspace
|
||||
!default.xcworkspace
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
|
||||
# Bundler
|
||||
.bundle
|
||||
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
|
||||
#
|
||||
# Note: if you ignore the Pods directory, make sure to uncomment
|
||||
# `pod install` in .travis.yml
|
||||
#
|
||||
# Pods/
|
||||
=======
|
||||
xcuserdata
|
||||
profile
|
||||
*.moved-aside
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# Taken from https://github.com/Alamofire/AlamofireImage
|
||||
language: objective-c
|
||||
osx_image: xcode7
|
||||
env:
|
||||
global:
|
||||
- LC_CTYPE=en_US.UTF-8
|
||||
- LANG=en_US.UTF-8
|
||||
matrix:
|
||||
- DESTINATION="OS=2.0,name=Apple Watch - 42mm" SCHEME="Locksmith watchOS" SDK=watchsimulator2.0 RUN_TESTS="NO" BUILD_EXAMPLE="NO" POD_LINT="YES"
|
||||
- DESTINATION="arch=x86_64" SCHEME="Locksmith OS X" SDK=macosx10.11 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
|
||||
# "build_example" should be YES but travis was giving me grief
|
||||
- DESTINATION="OS=9.0,name=iPad 2" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=9.0,name=iPhone 6" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
|
||||
- DESTINATION="OS=8.1,name=iPad 2" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.2,name=iPhone 4S" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.3,name=iPhone 5" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
- DESTINATION="OS=8.4,name=iPhone 5S" SCHEME="Locksmith iOS" SDK=iphonesimulator9.0 RUN_TESTS="YES" BUILD_EXAMPLE="NO" POD_LINT="NO"
|
||||
before_install:
|
||||
- gem install cocoapods --no-rdoc --no-ri --no-document --quiet
|
||||
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
|
||||
script:
|
||||
- set -o pipefail
|
||||
- xcodebuild -version
|
||||
|
||||
# Build and run tests in Debug and Release
|
||||
- if [ $RUN_TESTS == "YES" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Debug ONLY_ACTIVE_ARCH=NO test | xcpretty -c;
|
||||
fi
|
||||
- if [ $RUN_TESTS == "YES" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Release ONLY_ACTIVE_ARCH=NO test | xcpretty -c;
|
||||
fi
|
||||
|
||||
# Only build in Debug and Release
|
||||
- if [ $RUN_TESTS == "NO" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Debug ONLY_ACTIVE_ARCH=NO | xcpretty -c;
|
||||
fi
|
||||
- if [ $RUN_TESTS == "NO" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Release ONLY_ACTIVE_ARCH=NO | xcpretty -c;
|
||||
fi
|
||||
|
||||
# Build example in Debug and Release
|
||||
- if [ $BUILD_EXAMPLE == "YES" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "Locksmith iOS Example" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c;
|
||||
fi
|
||||
- if [ $BUILD_EXAMPLE == "YES" ]; then
|
||||
xcodebuild -workspace Locksmith.xcworkspace -scheme "Locksmith iOS Example" -sdk "$SDK" -destination "$DESTINATION"
|
||||
-configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty -c;
|
||||
fi
|
||||
|
||||
# Run pod lib lint quick if specified
|
||||
- if [ $POD_LINT == "YES" ]; then
|
||||
pod lib lint --quick;
|
||||
fi
|
||||
@@ -0,0 +1,312 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0E13A9A41BA3EE8700A06FF9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E13A9A31BA3EE8700A06FF9 /* AppDelegate.swift */; };
|
||||
0E13A9A61BA3EE8700A06FF9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E13A9A51BA3EE8700A06FF9 /* ViewController.swift */; };
|
||||
0E13A9A91BA3EE8700A06FF9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E13A9A71BA3EE8700A06FF9 /* Main.storyboard */; };
|
||||
0E13A9AB1BA3EE8700A06FF9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E13A9AA1BA3EE8700A06FF9 /* Assets.xcassets */; };
|
||||
0E13A9AE1BA3EE8700A06FF9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E13A9AC1BA3EE8700A06FF9 /* LaunchScreen.storyboard */; };
|
||||
0E13A9B81BA3EFFA00A06FF9 /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E13A9B71BA3EFFA00A06FF9 /* Locksmith.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
0E13A9BA1BA3F03D00A06FF9 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0E13A9A01BA3EE8700A06FF9 /* Locksmith iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Locksmith iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E13A9A31BA3EE8700A06FF9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
0E13A9A51BA3EE8700A06FF9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
0E13A9A81BA3EE8700A06FF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
0E13A9AA1BA3EE8700A06FF9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0E13A9AD1BA3EE8700A06FF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0E13A9AF1BA3EE8700A06FF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0E13A9B71BA3EFFA00A06FF9 /* Locksmith.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Locksmith.framework; path = "../build/Debug-iphoneos/Locksmith.framework"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
0E13A99D1BA3EE8700A06FF9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E13A9B81BA3EFFA00A06FF9 /* Locksmith.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0E13A9971BA3EE8600A06FF9 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E13A9B71BA3EFFA00A06FF9 /* Locksmith.framework */,
|
||||
0E13A9A21BA3EE8700A06FF9 /* Locksmith iOS Example */,
|
||||
0E13A9A11BA3EE8700A06FF9 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E13A9A11BA3EE8700A06FF9 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E13A9A01BA3EE8700A06FF9 /* Locksmith iOS Example.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E13A9A21BA3EE8700A06FF9 /* Locksmith iOS Example */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E13A9A31BA3EE8700A06FF9 /* AppDelegate.swift */,
|
||||
0E13A9A51BA3EE8700A06FF9 /* ViewController.swift */,
|
||||
0E13A9A71BA3EE8700A06FF9 /* Main.storyboard */,
|
||||
0E13A9AA1BA3EE8700A06FF9 /* Assets.xcassets */,
|
||||
0E13A9AC1BA3EE8700A06FF9 /* LaunchScreen.storyboard */,
|
||||
0E13A9AF1BA3EE8700A06FF9 /* Info.plist */,
|
||||
);
|
||||
path = "Locksmith iOS Example";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0E13A99F1BA3EE8700A06FF9 /* Locksmith iOS Example */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0E13A9B21BA3EE8700A06FF9 /* Build configuration list for PBXNativeTarget "Locksmith iOS Example" */;
|
||||
buildPhases = (
|
||||
0E13A99C1BA3EE8700A06FF9 /* Sources */,
|
||||
0E13A99D1BA3EE8700A06FF9 /* Frameworks */,
|
||||
0E13A99E1BA3EE8700A06FF9 /* Resources */,
|
||||
0E13A9BA1BA3F03D00A06FF9 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Locksmith iOS Example";
|
||||
productName = "Locksmith iOS Example";
|
||||
productReference = 0E13A9A01BA3EE8700A06FF9 /* Locksmith iOS Example.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0E13A9981BA3EE8600A06FF9 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Matthew Palmer";
|
||||
TargetAttributes = {
|
||||
0E13A99F1BA3EE8700A06FF9 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 0E13A99B1BA3EE8600A06FF9 /* Build configuration list for PBXProject "Locksmith iOS Example" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 0E13A9971BA3EE8600A06FF9;
|
||||
productRefGroup = 0E13A9A11BA3EE8700A06FF9 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0E13A99F1BA3EE8700A06FF9 /* Locksmith iOS Example */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
0E13A99E1BA3EE8700A06FF9 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E13A9AE1BA3EE8700A06FF9 /* LaunchScreen.storyboard in Resources */,
|
||||
0E13A9AB1BA3EE8700A06FF9 /* Assets.xcassets in Resources */,
|
||||
0E13A9A91BA3EE8700A06FF9 /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
0E13A99C1BA3EE8700A06FF9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E13A9A61BA3EE8700A06FF9 /* ViewController.swift in Sources */,
|
||||
0E13A9A41BA3EE8700A06FF9 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
0E13A9A71BA3EE8700A06FF9 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
0E13A9A81BA3EE8700A06FF9 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E13A9AC1BA3EE8700A06FF9 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
0E13A9AD1BA3EE8700A06FF9 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0E13A9B01BA3EE8700A06FF9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0E13A9B11BA3EE8700A06FF9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E13A9B31BA3EE8700A06FF9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = "Locksmith iOS Example/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0E13A9B41BA3EE8700A06FF9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = "Locksmith iOS Example/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
0E13A99B1BA3EE8600A06FF9 /* Build configuration list for PBXProject "Locksmith iOS Example" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E13A9B01BA3EE8700A06FF9 /* Debug */,
|
||||
0E13A9B11BA3EE8700A06FF9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0E13A9B21BA3EE8700A06FF9 /* Build configuration list for PBXNativeTarget "Locksmith iOS Example" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E13A9B31BA3EE8700A06FF9 /* Debug */,
|
||||
0E13A9B41BA3EE8700A06FF9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0E13A9981BA3EE8600A06FF9 /* Project object */;
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E13A99F1BA3EE8700A06FF9"
|
||||
BuildableName = "Locksmith iOS Example.app"
|
||||
BlueprintName = "Locksmith iOS Example"
|
||||
ReferencedContainer = "container:Locksmith iOS Example.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E13A99F1BA3EE8700A06FF9"
|
||||
BuildableName = "Locksmith iOS Example.app"
|
||||
BlueprintName = "Locksmith iOS Example"
|
||||
ReferencedContainer = "container:Locksmith iOS Example.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E13A99F1BA3EE8700A06FF9"
|
||||
BuildableName = "Locksmith iOS Example.app"
|
||||
BlueprintName = "Locksmith iOS Example"
|
||||
ReferencedContainer = "container:Locksmith iOS Example.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E13A99F1BA3EE8700A06FF9"
|
||||
BuildableName = "Locksmith iOS Example.app"
|
||||
BlueprintName = "Locksmith iOS Example"
|
||||
ReferencedContainer = "container:Locksmith iOS Example.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Locksmith iOS Example
|
||||
//
|
||||
// Created by Matthew Palmer on 12/09/2015.
|
||||
// Copyright © 2015 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Locksmith
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
struct TwitterAccount: ReadableSecureStorable,
|
||||
CreateableSecureStorable,
|
||||
DeleteableSecureStorable,
|
||||
GenericPasswordSecureStorable {
|
||||
let username: String
|
||||
let password: String
|
||||
|
||||
let service = "Twitter"
|
||||
var account: String { return username }
|
||||
var data: [String: AnyObject] {
|
||||
return ["password": password]
|
||||
}
|
||||
}
|
||||
|
||||
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
|
||||
|
||||
// CreateableSecureStorable lets us create the account in the keychain
|
||||
try! account.createInSecureStore()
|
||||
|
||||
// ReadableSecureStorable lets us read the account from the keychain
|
||||
let result = account.readFromSecureStore()
|
||||
|
||||
print("Result: \(result) \(result?.data)")
|
||||
|
||||
// DeleteableSecureStorable lets us delete the account from the keychain
|
||||
try! account.deleteFromSecureStore()
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8173.3" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8142"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Locksmith_iOS_Example" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Locksmith Example—see AppDelegate.swift" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mhD-FU-PNz">
|
||||
<rect key="frame" x="132" y="289" width="337" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="mhD-FU-PNz" secondAttribute="bottom" constant="290" id="0ha-lJ-FNO"/>
|
||||
<constraint firstItem="mhD-FU-PNz" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="112" id="3eM-ve-Fq1"/>
|
||||
<constraint firstItem="mhD-FU-PNz" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="269" id="ZCS-J6-lWh"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="mhD-FU-PNz" secondAttribute="trailing" constant="111" id="o19-IS-K4H"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.matthewpalmer.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// Locksmith iOS Example
|
||||
//
|
||||
// Created by Matthew Palmer on 12/09/2015.
|
||||
// Copyright © 2015 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Matthew Palmer
|
||||
Copyright (c) 2015 matthewpalmer <matt@matthewpalmer.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -9,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,23 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Locksmith"
|
||||
s.version = "2.0.1"
|
||||
s.summary = "Locksmith is a powerful, protocol-oriented library for working with the keychain in Swift."
|
||||
s.description = <<-DESC
|
||||
Locksmith is a powerful, protocol-oriented library for working with the iOS, Mac OS X, watchOS, and tvOS keychain in Swift. It provides extensive support for a lot of different keychain requests, and extensively uses Swift-native concepts.
|
||||
DESC
|
||||
s.homepage = "https://github.com/matthewpalmer/Locksmith"
|
||||
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.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
|
||||
s.requires_arc = true
|
||||
|
||||
s.source_files = 'Source/*.{m,h,swift}'
|
||||
|
||||
s.frameworks = 'Foundation', 'Security'
|
||||
end
|
||||
+562
-214
@@ -7,50 +7,94 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0E77303F19FCC05300CD6D93 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E77303E19FCC05300CD6D93 /* AppDelegate.swift */; };
|
||||
0E77304119FCC05300CD6D93 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E77304019FCC05300CD6D93 /* ViewController.swift */; };
|
||||
0E77304419FCC05300CD6D93 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E77304219FCC05300CD6D93 /* Main.storyboard */; };
|
||||
0E77304619FCC05300CD6D93 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E77304519FCC05300CD6D93 /* Images.xcassets */; };
|
||||
0E77304919FCC05300CD6D93 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E77304719FCC05300CD6D93 /* LaunchScreen.xib */; };
|
||||
0E77305519FCC05300CD6D93 /* LocksmithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E77305419FCC05300CD6D93 /* LocksmithTests.swift */; };
|
||||
0E77306019FCC06600CD6D93 /* LocksmithRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E77305E19FCC06600CD6D93 /* LocksmithRequest.swift */; };
|
||||
0E77306119FCC06600CD6D93 /* Locksmith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E77305F19FCC06600CD6D93 /* Locksmith.swift */; };
|
||||
0EC25C631BA385AB004191AF /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC25C591BA385AA004191AF /* Locksmith.framework */; };
|
||||
0EC25C7F1BA385F6004191AF /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC25C751BA385F6004191AF /* Locksmith.framework */; };
|
||||
0EC25C8D1BA38648004191AF /* LocksmithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C491BA38539004191AF /* LocksmithTests.swift */; };
|
||||
0EC25C8E1BA38649004191AF /* LocksmithTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C491BA38539004191AF /* LocksmithTests.swift */; };
|
||||
0EC25C8F1BA3864C004191AF /* Dictionary_Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C401BA38539004191AF /* Dictionary_Initializers.swift */; };
|
||||
0EC25C901BA3864D004191AF /* Dictionary_Initializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C401BA38539004191AF /* Dictionary_Initializers.swift */; };
|
||||
0EC25C911BA3864F004191AF /* Locksmith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C421BA38539004191AF /* Locksmith.swift */; };
|
||||
0EC25C921BA38651004191AF /* Locksmith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C421BA38539004191AF /* Locksmith.swift */; };
|
||||
0EC25C931BA38653004191AF /* LocksmithAccessibleOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C431BA38539004191AF /* LocksmithAccessibleOption.swift */; };
|
||||
0EC25C941BA38654004191AF /* LocksmithAccessibleOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C431BA38539004191AF /* LocksmithAccessibleOption.swift */; };
|
||||
0EC25C951BA38657004191AF /* LocksmithError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C441BA38539004191AF /* LocksmithError.swift */; };
|
||||
0EC25C961BA38659004191AF /* LocksmithError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C441BA38539004191AF /* LocksmithError.swift */; };
|
||||
0EC25C971BA3865B004191AF /* LocksmithInternetAuthenticationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C451BA38539004191AF /* LocksmithInternetAuthenticationType.swift */; };
|
||||
0EC25C981BA3865C004191AF /* LocksmithInternetAuthenticationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C451BA38539004191AF /* LocksmithInternetAuthenticationType.swift */; };
|
||||
0EC25C991BA3865F004191AF /* LocksmithInternetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C461BA38539004191AF /* LocksmithInternetProtocol.swift */; };
|
||||
0EC25C9A1BA38660004191AF /* LocksmithInternetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C461BA38539004191AF /* LocksmithInternetProtocol.swift */; };
|
||||
0EC25C9B1BA38662004191AF /* LocksmithSecurityClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C471BA38539004191AF /* LocksmithSecurityClass.swift */; };
|
||||
0EC25C9C1BA38663004191AF /* LocksmithSecurityClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC25C471BA38539004191AF /* LocksmithSecurityClass.swift */; };
|
||||
0EC25C9F1BA389CB004191AF /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0EC25C9E1BA389CB004191AF /* Info.plist */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
0E77304F19FCC05300CD6D93 /* PBXContainerItemProxy */ = {
|
||||
0EC25C641BA385AB004191AF /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 0E77303119FCC05300CD6D93 /* Project object */;
|
||||
containerPortal = BFFB19C71A4870A300CCFFC3 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0E77303819FCC05300CD6D93;
|
||||
remoteInfo = Locksmith;
|
||||
remoteGlobalIDString = 0EC25C581BA385AA004191AF;
|
||||
remoteInfo = "Locksmith iOS";
|
||||
};
|
||||
0EC25C801BA385F6004191AF /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = BFFB19C71A4870A300CCFFC3 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0EC25C741BA385F6004191AF;
|
||||
remoteInfo = "Locksmith OS X";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0E77303919FCC05300CD6D93 /* Locksmith.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locksmith.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E77303D19FCC05300CD6D93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0E77303E19FCC05300CD6D93 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
0E77304019FCC05300CD6D93 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
0E77304319FCC05300CD6D93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
0E77304519FCC05300CD6D93 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
0E77304819FCC05300CD6D93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
0E77304E19FCC05300CD6D93 /* LocksmithTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LocksmithTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E77305319FCC05300CD6D93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0E77305419FCC05300CD6D93 /* LocksmithTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocksmithTests.swift; sourceTree = "<group>"; };
|
||||
0E77305E19FCC06600CD6D93 /* LocksmithRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithRequest.swift; sourceTree = "<group>"; };
|
||||
0E77305F19FCC06600CD6D93 /* Locksmith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locksmith.swift; sourceTree = "<group>"; };
|
||||
0EC25C401BA38539004191AF /* Dictionary_Initializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary_Initializers.swift; sourceTree = "<group>"; };
|
||||
0EC25C421BA38539004191AF /* Locksmith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locksmith.swift; sourceTree = "<group>"; };
|
||||
0EC25C431BA38539004191AF /* LocksmithAccessibleOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithAccessibleOption.swift; sourceTree = "<group>"; };
|
||||
0EC25C441BA38539004191AF /* LocksmithError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithError.swift; sourceTree = "<group>"; };
|
||||
0EC25C451BA38539004191AF /* LocksmithInternetAuthenticationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithInternetAuthenticationType.swift; sourceTree = "<group>"; };
|
||||
0EC25C461BA38539004191AF /* LocksmithInternetProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithInternetProtocol.swift; sourceTree = "<group>"; };
|
||||
0EC25C471BA38539004191AF /* LocksmithSecurityClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithSecurityClass.swift; sourceTree = "<group>"; };
|
||||
0EC25C491BA38539004191AF /* LocksmithTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocksmithTests.swift; sourceTree = "<group>"; };
|
||||
0EC25C591BA385AA004191AF /* Locksmith.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Locksmith.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0EC25C621BA385AB004191AF /* Locksmith iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Locksmith iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0EC25C751BA385F6004191AF /* Locksmith.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Locksmith.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0EC25C7E1BA385F6004191AF /* Locksmith OS X Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Locksmith OS X Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0EC25C9D1BA389BD004191AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0EC25C9E1BA389CB004191AF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0EC25CA71BA39C9F004191AF /* Locksmith.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Locksmith.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
0E77303619FCC05300CD6D93 /* Frameworks */ = {
|
||||
0EC25C551BA385AA004191AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0E77304B19FCC05300CD6D93 /* Frameworks */ = {
|
||||
0EC25C5F1BA385AB004191AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EC25C631BA385AB004191AF /* Locksmith.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C711BA385F6004191AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C7B1BA385F6004191AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EC25C7F1BA385F6004191AF /* Locksmith.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25CA31BA39C9F004191AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -60,151 +104,263 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0E77303019FCC05300CD6D93 = {
|
||||
0EC25C481BA38539004191AF /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77303B19FCC05300CD6D93 /* Locksmith */,
|
||||
0E77305119FCC05300CD6D93 /* LocksmithTests */,
|
||||
0E77303A19FCC05300CD6D93 /* Products */,
|
||||
0EC25C401BA38539004191AF /* Dictionary_Initializers.swift */,
|
||||
0EC25C421BA38539004191AF /* Locksmith.swift */,
|
||||
0EC25C431BA38539004191AF /* LocksmithAccessibleOption.swift */,
|
||||
0EC25C441BA38539004191AF /* LocksmithError.swift */,
|
||||
0EC25C451BA38539004191AF /* LocksmithInternetAuthenticationType.swift */,
|
||||
0EC25C461BA38539004191AF /* LocksmithInternetProtocol.swift */,
|
||||
0EC25C471BA38539004191AF /* LocksmithSecurityClass.swift */,
|
||||
0EC25CA01BA38A05004191AF /* Supporting Files */,
|
||||
);
|
||||
path = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0EC25C4A1BA38539004191AF /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC25C491BA38539004191AF /* LocksmithTests.swift */,
|
||||
0EC25CA11BA38A0B004191AF /* Supporting Files */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0EC25CA01BA38A05004191AF /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC25C9E1BA389CB004191AF /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0EC25CA11BA38A0B004191AF /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC25C9D1BA389BD004191AF /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFFB19C61A4870A300CCFFC3 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0EC25C481BA38539004191AF /* Source */,
|
||||
0EC25C4A1BA38539004191AF /* Tests */,
|
||||
BFFB19D11A4870A300CCFFC3 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77303A19FCC05300CD6D93 /* Products */ = {
|
||||
BFFB19D11A4870A300CCFFC3 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77303919FCC05300CD6D93 /* Locksmith.app */,
|
||||
0E77304E19FCC05300CD6D93 /* LocksmithTests.xctest */,
|
||||
0EC25C591BA385AA004191AF /* Locksmith.framework */,
|
||||
0EC25C621BA385AB004191AF /* Locksmith iOS Tests.xctest */,
|
||||
0EC25C751BA385F6004191AF /* Locksmith.framework */,
|
||||
0EC25C7E1BA385F6004191AF /* Locksmith OS X Tests.xctest */,
|
||||
0EC25CA71BA39C9F004191AF /* Locksmith.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77303B19FCC05300CD6D93 /* Locksmith */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77303E19FCC05300CD6D93 /* AppDelegate.swift */,
|
||||
0E77304019FCC05300CD6D93 /* ViewController.swift */,
|
||||
0E77305E19FCC06600CD6D93 /* LocksmithRequest.swift */,
|
||||
0E77305F19FCC06600CD6D93 /* Locksmith.swift */,
|
||||
0E77304219FCC05300CD6D93 /* Main.storyboard */,
|
||||
0E77304519FCC05300CD6D93 /* Images.xcassets */,
|
||||
0E77304719FCC05300CD6D93 /* LaunchScreen.xib */,
|
||||
0E77303C19FCC05300CD6D93 /* Supporting Files */,
|
||||
);
|
||||
path = Locksmith;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77303C19FCC05300CD6D93 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77303D19FCC05300CD6D93 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77305119FCC05300CD6D93 /* LocksmithTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77305419FCC05300CD6D93 /* LocksmithTests.swift */,
|
||||
0E77305219FCC05300CD6D93 /* Supporting Files */,
|
||||
);
|
||||
path = LocksmithTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77305219FCC05300CD6D93 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E77305319FCC05300CD6D93 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0E77303819FCC05300CD6D93 /* Locksmith */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0E77305819FCC05300CD6D93 /* Build configuration list for PBXNativeTarget "Locksmith" */;
|
||||
buildPhases = (
|
||||
0E77303519FCC05300CD6D93 /* Sources */,
|
||||
0E77303619FCC05300CD6D93 /* Frameworks */,
|
||||
0E77303719FCC05300CD6D93 /* Resources */,
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
0EC25C561BA385AA004191AF /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Locksmith;
|
||||
productName = Locksmith;
|
||||
productReference = 0E77303919FCC05300CD6D93 /* Locksmith.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0E77304D19FCC05300CD6D93 /* LocksmithTests */ = {
|
||||
0EC25C721BA385F6004191AF /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25CA41BA39C9F004191AF /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0EC25C581BA385AA004191AF /* Locksmith iOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0E77305B19FCC05300CD6D93 /* Build configuration list for PBXNativeTarget "LocksmithTests" */;
|
||||
buildConfigurationList = 0EC25C6A1BA385AB004191AF /* Build configuration list for PBXNativeTarget "Locksmith iOS" */;
|
||||
buildPhases = (
|
||||
0E77304A19FCC05300CD6D93 /* Sources */,
|
||||
0E77304B19FCC05300CD6D93 /* Frameworks */,
|
||||
0E77304C19FCC05300CD6D93 /* Resources */,
|
||||
0EC25C541BA385AA004191AF /* Sources */,
|
||||
0EC25C551BA385AA004191AF /* Frameworks */,
|
||||
0EC25C561BA385AA004191AF /* Headers */,
|
||||
0EC25C571BA385AA004191AF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
0E77305019FCC05300CD6D93 /* PBXTargetDependency */,
|
||||
);
|
||||
name = LocksmithTests;
|
||||
productName = LocksmithTests;
|
||||
productReference = 0E77304E19FCC05300CD6D93 /* LocksmithTests.xctest */;
|
||||
name = "Locksmith iOS";
|
||||
productName = "Locksmith iOS";
|
||||
productReference = 0EC25C591BA385AA004191AF /* Locksmith.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
0EC25C611BA385AB004191AF /* Locksmith iOS Tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0EC25C6D1BA385AB004191AF /* Build configuration list for PBXNativeTarget "Locksmith iOS Tests" */;
|
||||
buildPhases = (
|
||||
0EC25C5E1BA385AB004191AF /* Sources */,
|
||||
0EC25C5F1BA385AB004191AF /* Frameworks */,
|
||||
0EC25C601BA385AB004191AF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
0EC25C651BA385AB004191AF /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Locksmith iOS Tests";
|
||||
productName = "Locksmith iOSTests";
|
||||
productReference = 0EC25C621BA385AB004191AF /* Locksmith iOS Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
0EC25C741BA385F6004191AF /* Locksmith OS X */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0EC25C861BA385F6004191AF /* Build configuration list for PBXNativeTarget "Locksmith OS X" */;
|
||||
buildPhases = (
|
||||
0EC25C701BA385F6004191AF /* Sources */,
|
||||
0EC25C711BA385F6004191AF /* Frameworks */,
|
||||
0EC25C721BA385F6004191AF /* Headers */,
|
||||
0EC25C731BA385F6004191AF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Locksmith OS X";
|
||||
productName = "Locksmith OS X";
|
||||
productReference = 0EC25C751BA385F6004191AF /* Locksmith.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
0EC25C7D1BA385F6004191AF /* Locksmith OS X Tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0EC25C891BA385F6004191AF /* Build configuration list for PBXNativeTarget "Locksmith OS X Tests" */;
|
||||
buildPhases = (
|
||||
0EC25C7A1BA385F6004191AF /* Sources */,
|
||||
0EC25C7B1BA385F6004191AF /* Frameworks */,
|
||||
0EC25C7C1BA385F6004191AF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
0EC25C811BA385F6004191AF /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Locksmith OS X Tests";
|
||||
productName = "Locksmith OS XTests";
|
||||
productReference = 0EC25C7E1BA385F6004191AF /* Locksmith OS X Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
0EC25CA61BA39C9F004191AF /* Locksmith watchOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0EC25CAE1BA39CA0004191AF /* Build configuration list for PBXNativeTarget "Locksmith watchOS" */;
|
||||
buildPhases = (
|
||||
0EC25CA21BA39C9F004191AF /* Sources */,
|
||||
0EC25CA31BA39C9F004191AF /* Frameworks */,
|
||||
0EC25CA41BA39C9F004191AF /* Headers */,
|
||||
0EC25CA51BA39C9F004191AF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Locksmith watchOS";
|
||||
productName = "Locksmith watchOS";
|
||||
productReference = 0EC25CA71BA39C9F004191AF /* Locksmith.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0E77303119FCC05300CD6D93 /* Project object */ = {
|
||||
BFFB19C71A4870A300CCFFC3 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = "Matthew Palmer";
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Mathew Palmer";
|
||||
TargetAttributes = {
|
||||
0E77303819FCC05300CD6D93 = {
|
||||
CreatedOnToolsVersion = 6.1;
|
||||
0EC25C581BA385AA004191AF = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
0E77304D19FCC05300CD6D93 = {
|
||||
CreatedOnToolsVersion = 6.1;
|
||||
TestTargetID = 0E77303819FCC05300CD6D93;
|
||||
0EC25C611BA385AB004191AF = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
0EC25C741BA385F6004191AF = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
0EC25C7D1BA385F6004191AF = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
0EC25CA61BA39C9F004191AF = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 0E77303419FCC05300CD6D93 /* Build configuration list for PBXProject "Locksmith" */;
|
||||
buildConfigurationList = BFFB19CA1A4870A300CCFFC3 /* Build configuration list for PBXProject "Locksmith" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 0E77303019FCC05300CD6D93;
|
||||
productRefGroup = 0E77303A19FCC05300CD6D93 /* Products */;
|
||||
mainGroup = BFFB19C61A4870A300CCFFC3;
|
||||
productRefGroup = BFFB19D11A4870A300CCFFC3 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0E77303819FCC05300CD6D93 /* Locksmith */,
|
||||
0E77304D19FCC05300CD6D93 /* LocksmithTests */,
|
||||
0EC25C581BA385AA004191AF /* Locksmith iOS */,
|
||||
0EC25C611BA385AB004191AF /* Locksmith iOS Tests */,
|
||||
0EC25C741BA385F6004191AF /* Locksmith OS X */,
|
||||
0EC25C7D1BA385F6004191AF /* Locksmith OS X Tests */,
|
||||
0EC25CA61BA39C9F004191AF /* Locksmith watchOS */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
0E77303719FCC05300CD6D93 /* Resources */ = {
|
||||
0EC25C571BA385AA004191AF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E77304419FCC05300CD6D93 /* Main.storyboard in Resources */,
|
||||
0E77304919FCC05300CD6D93 /* LaunchScreen.xib in Resources */,
|
||||
0E77304619FCC05300CD6D93 /* Images.xcassets in Resources */,
|
||||
0EC25C9F1BA389CB004191AF /* Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0E77304C19FCC05300CD6D93 /* Resources */ = {
|
||||
0EC25C601BA385AB004191AF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C731BA385F6004191AF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C7C1BA385F6004191AF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25CA51BA39C9F004191AF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -214,56 +370,263 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
0E77303519FCC05300CD6D93 /* Sources */ = {
|
||||
0EC25C541BA385AA004191AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E77306119FCC06600CD6D93 /* Locksmith.swift in Sources */,
|
||||
0E77304119FCC05300CD6D93 /* ViewController.swift in Sources */,
|
||||
0E77306019FCC06600CD6D93 /* LocksmithRequest.swift in Sources */,
|
||||
0E77303F19FCC05300CD6D93 /* AppDelegate.swift in Sources */,
|
||||
0EC25C931BA38653004191AF /* LocksmithAccessibleOption.swift in Sources */,
|
||||
0EC25C9B1BA38662004191AF /* LocksmithSecurityClass.swift in Sources */,
|
||||
0EC25C911BA3864F004191AF /* Locksmith.swift in Sources */,
|
||||
0EC25C8F1BA3864C004191AF /* Dictionary_Initializers.swift in Sources */,
|
||||
0EC25C951BA38657004191AF /* LocksmithError.swift in Sources */,
|
||||
0EC25C971BA3865B004191AF /* LocksmithInternetAuthenticationType.swift in Sources */,
|
||||
0EC25C991BA3865F004191AF /* LocksmithInternetProtocol.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0E77304A19FCC05300CD6D93 /* Sources */ = {
|
||||
0EC25C5E1BA385AB004191AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EC25C8E1BA38649004191AF /* LocksmithTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C701BA385F6004191AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EC25C941BA38654004191AF /* LocksmithAccessibleOption.swift in Sources */,
|
||||
0EC25C9C1BA38663004191AF /* LocksmithSecurityClass.swift in Sources */,
|
||||
0EC25C921BA38651004191AF /* Locksmith.swift in Sources */,
|
||||
0EC25C901BA3864D004191AF /* Dictionary_Initializers.swift in Sources */,
|
||||
0EC25C961BA38659004191AF /* LocksmithError.swift in Sources */,
|
||||
0EC25C981BA3865C004191AF /* LocksmithInternetAuthenticationType.swift in Sources */,
|
||||
0EC25C9A1BA38660004191AF /* LocksmithInternetProtocol.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25C7A1BA385F6004191AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0EC25C8D1BA38648004191AF /* LocksmithTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
0EC25CA21BA39C9F004191AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E77305519FCC05300CD6D93 /* LocksmithTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
0E77305019FCC05300CD6D93 /* PBXTargetDependency */ = {
|
||||
0EC25C651BA385AB004191AF /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0E77303819FCC05300CD6D93 /* Locksmith */;
|
||||
targetProxy = 0E77304F19FCC05300CD6D93 /* PBXContainerItemProxy */;
|
||||
target = 0EC25C581BA385AA004191AF /* Locksmith iOS */;
|
||||
targetProxy = 0EC25C641BA385AB004191AF /* PBXContainerItemProxy */;
|
||||
};
|
||||
0EC25C811BA385F6004191AF /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0EC25C741BA385F6004191AF /* Locksmith OS X */;
|
||||
targetProxy = 0EC25C801BA385F6004191AF /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
0E77304219FCC05300CD6D93 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
0E77304319FCC05300CD6D93 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E77304719FCC05300CD6D93 /* LaunchScreen.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
0E77304819FCC05300CD6D93 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
0E77305619FCC05300CD6D93 /* Debug */ = {
|
||||
0EC25C6B1BA385AB004191AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EC25C6C1BA385AB004191AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0EC25C6E1BA385AB004191AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOSTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EC25C6F1BA385AB004191AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOSTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0EC25C871BA385F6004191AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_TESTABILITY = YES;
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-OS-X";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EC25C881BA385F6004191AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-OS-X";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0EC25C8A1BA385F6004191AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-OS-XTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EC25C8B1BA385F6004191AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-OS-XTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0EC25CAC1BA39CA0004191AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-watchOS";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0EC25CAD1BA39CA0004191AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Source/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-watchOS";
|
||||
PRODUCT_NAME = Locksmith;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
BFFB19E41A4870A300CCFFC3 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -282,7 +645,9 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
@@ -297,16 +662,20 @@
|
||||
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;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0E77305719FCC05300CD6D93 /* Release */ = {
|
||||
BFFB19E51A4870A300CCFFC3 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -325,6 +694,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
@@ -334,97 +704,75 @@
|
||||
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;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E77305919FCC05300CD6D93 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = Locksmith/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0E77305A19FCC05300CD6D93 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = Locksmith/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E77305C19FCC05300CD6D93 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = LocksmithTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Locksmith.app/Locksmith";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
0E77305D19FCC05300CD6D93 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = LocksmithTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Locksmith.app/Locksmith";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
0E77303419FCC05300CD6D93 /* Build configuration list for PBXProject "Locksmith" */ = {
|
||||
0EC25C6A1BA385AB004191AF /* Build configuration list for PBXNativeTarget "Locksmith iOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E77305619FCC05300CD6D93 /* Debug */,
|
||||
0E77305719FCC05300CD6D93 /* Release */,
|
||||
0EC25C6B1BA385AB004191AF /* Debug */,
|
||||
0EC25C6C1BA385AB004191AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0E77305819FCC05300CD6D93 /* Build configuration list for PBXNativeTarget "Locksmith" */ = {
|
||||
0EC25C6D1BA385AB004191AF /* Build configuration list for PBXNativeTarget "Locksmith iOS Tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E77305919FCC05300CD6D93 /* Debug */,
|
||||
0E77305A19FCC05300CD6D93 /* Release */,
|
||||
0EC25C6E1BA385AB004191AF /* Debug */,
|
||||
0EC25C6F1BA385AB004191AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0EC25C861BA385F6004191AF /* Build configuration list for PBXNativeTarget "Locksmith OS X" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0EC25C871BA385F6004191AF /* Debug */,
|
||||
0EC25C881BA385F6004191AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0EC25C891BA385F6004191AF /* Build configuration list for PBXNativeTarget "Locksmith OS X Tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0EC25C8A1BA385F6004191AF /* Debug */,
|
||||
0EC25C8B1BA385F6004191AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0EC25CAE1BA39CA0004191AF /* Build configuration list for PBXNativeTarget "Locksmith watchOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0EC25CAC1BA39CA0004191AF /* Debug */,
|
||||
0EC25CAD1BA39CA0004191AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
0E77305B19FCC05300CD6D93 /* Build configuration list for PBXNativeTarget "LocksmithTests" */ = {
|
||||
BFFB19CA1A4870A300CCFFC3 /* Build configuration list for PBXProject "Locksmith" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E77305C19FCC05300CD6D93 /* Debug */,
|
||||
0E77305D19FCC05300CD6D93 /* Release */,
|
||||
BFFB19E41A4870A300CCFFC3 /* Debug */,
|
||||
BFFB19E51A4870A300CCFFC3 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0E77303119FCC05300CD6D93 /* Project object */;
|
||||
rootObject = BFFB19C71A4870A300CCFFC3 /* Project object */;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>6AAF752A-6001-46C3-BB3E-4EA76720DE90</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>Locksmith</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>9E5DC2F442C5D1D821707804A23B5D894E49AF44</key>
|
||||
<string>https://github.com/matthewpalmer/Locksmith</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>Locksmith.xcodeproj</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>9E5DC2F442C5D1D821707804A23B5D894E49AF44</key>
|
||||
<string>../..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/matthewpalmer/Locksmith</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>9E5DC2F442C5D1D821707804A23B5D894E49AF44</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>9E5DC2F442C5D1D821707804A23B5D894E49AF44</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>Locksmith</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Binary file not shown.
+27
-38
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0610"
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -14,41 +14,27 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77303819FCC05300CD6D93"
|
||||
BuildableName = "Locksmith.app"
|
||||
BlueprintName = "Locksmith"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77304D19FCC05300CD6D93"
|
||||
BuildableName = "LocksmithTests.xctest"
|
||||
BlueprintName = "LocksmithTests"
|
||||
BlueprintIdentifier = "0EC25C741BA385F6004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith OS X"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77304D19FCC05300CD6D93"
|
||||
BuildableName = "LocksmithTests.xctest"
|
||||
BlueprintName = "LocksmithTests"
|
||||
BlueprintIdentifier = "0EC25C7D1BA385F6004191AF"
|
||||
BuildableName = "Locksmith OS X Tests.xctest"
|
||||
BlueprintName = "Locksmith OS X Tests"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
@@ -56,49 +42,52 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77303819FCC05300CD6D93"
|
||||
BuildableName = "Locksmith.app"
|
||||
BlueprintName = "Locksmith"
|
||||
BlueprintIdentifier = "0EC25C741BA385F6004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith OS X"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77303819FCC05300CD6D93"
|
||||
BuildableName = "Locksmith.app"
|
||||
BlueprintName = "Locksmith"
|
||||
BlueprintIdentifier = "0EC25C741BA385F6004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith OS X"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E77303819FCC05300CD6D93"
|
||||
BuildableName = "Locksmith.app"
|
||||
BlueprintName = "Locksmith"
|
||||
BlueprintIdentifier = "0EC25C741BA385F6004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith OS X"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25C581BA385AA004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith iOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25C611BA385AB004191AF"
|
||||
BuildableName = "Locksmith iOS Tests.xctest"
|
||||
BlueprintName = "Locksmith iOS Tests"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25C581BA385AA004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith iOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25C581BA385AA004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith iOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25C581BA385AA004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith iOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25CA61BA39C9F004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith watchOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25CA61BA39C9F004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith watchOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0EC25CA61BA39C9F004191AF"
|
||||
BuildableName = "Locksmith.framework"
|
||||
BlueprintName = "Locksmith watchOS"
|
||||
ReferencedContainer = "container:Locksmith.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Locksmith.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>0E77303819FCC05300CD6D93</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>0E77304D19FCC05300CD6D93</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<Group
|
||||
location = "container:"
|
||||
name = "Examples">
|
||||
<FileRef
|
||||
location = "group:Examples/Locksmith iOS Example/Locksmith iOS Example.xcodeproj">
|
||||
</FileRef>
|
||||
</Group>
|
||||
<FileRef
|
||||
location = "group:LICENSE">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Locksmith.podspec">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Locksmith.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:README.md">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Locksmith
|
||||
//
|
||||
// Created by Matthew Palmer on 26/10/2014.
|
||||
// Copyright (c) 2014 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(application: UIApplication) {
|
||||
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6250" systemVersion="14A388b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2014 Matthew Palmer. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Locksmith" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6221" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6213"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Locksmith" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SK4-b0-x78">
|
||||
<rect key="frame" x="283" y="98" width="34" height="30"/>
|
||||
<state key="normal" title="Save">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="save:" destination="BYZ-38-t0r" eventType="touchUpInside" id="x0D-Hg-awu"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5KA-Tp-Vu9">
|
||||
<rect key="frame" x="283" y="202" width="35" height="30"/>
|
||||
<state key="normal" title="Load">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="loadData:" destination="BYZ-38-t0r" eventType="touchUpInside" id="atD-QV-TTn"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gwg-uJ-dG8">
|
||||
<rect key="frame" x="275" y="150" width="51" height="30"/>
|
||||
<state key="normal" title="Update">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="update:" destination="BYZ-38-t0r" eventType="touchUpInside" id="dWB-sq-oQP"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tpV-X6-FVb">
|
||||
<rect key="frame" x="278" y="254" width="44" height="30"/>
|
||||
<state key="normal" title="Delete">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="deleteData:" destination="BYZ-38-t0r" eventType="touchUpInside" id="gee-TK-WNb"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="tpV-X6-FVb" firstAttribute="top" secondItem="5KA-Tp-Vu9" secondAttribute="bottom" constant="22" id="3qD-fa-LXx"/>
|
||||
<constraint firstItem="gwg-uJ-dG8" firstAttribute="top" secondItem="SK4-b0-x78" secondAttribute="bottom" constant="22" id="IZm-Sn-eJP"/>
|
||||
<constraint firstItem="5KA-Tp-Vu9" firstAttribute="top" secondItem="gwg-uJ-dG8" secondAttribute="bottom" constant="22" id="Qqg-BG-zp2"/>
|
||||
<constraint firstAttribute="centerY" secondItem="SK4-b0-x78" secondAttribute="centerY" constant="187" id="Ref-uw-o2A"/>
|
||||
<constraint firstItem="SK4-b0-x78" firstAttribute="centerX" secondItem="gwg-uJ-dG8" secondAttribute="centerX" constant="-0.5" id="TJJ-p7-8f7"/>
|
||||
<constraint firstItem="gwg-uJ-dG8" firstAttribute="centerX" secondItem="tpV-X6-FVb" secondAttribute="centerX" constant="0.5" id="cMH-qn-qVu"/>
|
||||
<constraint firstAttribute="centerX" secondItem="SK4-b0-x78" secondAttribute="centerX" id="jgz-mW-BGM"/>
|
||||
<constraint firstItem="5KA-Tp-Vu9" firstAttribute="centerX" secondItem="gwg-uJ-dG8" secondAttribute="centerX" id="rco-0g-m08"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -1,236 +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"
|
||||
|
||||
class Locksmith: NSObject {
|
||||
// MARK: Perform request
|
||||
class func performRequest(request: LocksmithRequest) -> (NSDictionary?, NSError?) {
|
||||
let type = request.type
|
||||
var result: Unmanaged<AnyObject>? = nil
|
||||
var status: OSStatus?
|
||||
|
||||
var parsedRequest: NSMutableDictionary = parseRequest(request)
|
||||
|
||||
var requestReference = parsedRequest as CFDictionaryRef
|
||||
|
||||
switch type {
|
||||
case .Create:
|
||||
status = SecItemAdd(requestReference, &result)
|
||||
case .Read:
|
||||
status = SecItemCopyMatching(requestReference, &result)
|
||||
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?.takeUnretainedValue() 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, result: UnsafeMutablePointer<Unmanaged<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 = SecItemAdd(request, result)
|
||||
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
|
||||
}
|
||||
|
||||
enum LocksmithErrorMessage: String {
|
||||
case RequestNotSet = "keychainRequest was not set."
|
||||
case TypeNotFound = "The type of request given was undefined."
|
||||
}
|
||||
|
||||
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
|
||||
default:
|
||||
return "Error message for code \(statusCode) not set"
|
||||
}
|
||||
}
|
||||
|
||||
private class func parseRequest(request: LocksmithRequest) -> NSMutableDictionary {
|
||||
var parsedRequest = NSMutableDictionary()
|
||||
|
||||
parsedRequest.setOptional(request.userAccount, forKey: String(kSecAttrAccount))
|
||||
parsedRequest.setOptional(request.group, forKey: String(kSecAttrAccessGroup))
|
||||
parsedRequest.setOptional(request.service, forKey: String(kSecAttrService))
|
||||
|
||||
// parsedRequest.setOptional(Locksmith.securityCode(request.securityClass), forKey: String(kSecClass))
|
||||
parsedRequest.setOptional(kSecClassGenericPassword, forKey: String(kSecClass))
|
||||
|
||||
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
|
||||
default:
|
||||
return kSecClassGenericPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Convenient Class Methods
|
||||
extension Locksmith {
|
||||
class func saveData(data: Dictionary<String, String>, forKey key: String, inService service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
|
||||
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key, requestType: .Create, data: data)
|
||||
return Locksmith.performRequest(saveRequest)
|
||||
}
|
||||
|
||||
class func loadData(forKey key: String, inService service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
|
||||
let readRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key)
|
||||
return Locksmith.performRequest(readRequest)
|
||||
}
|
||||
|
||||
class func deleteData(forKey key: String, inService service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
|
||||
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key, requestType: .Delete)
|
||||
return Locksmith.performRequest(deleteRequest)
|
||||
}
|
||||
|
||||
class func updateData(data: Dictionary<String, String>, forKey key: String, inService service: String, forUserAccount userAccount: String) -> (NSDictionary?, NSError?) {
|
||||
let updateRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key, requestType: .Update, data: data)
|
||||
return Locksmith.performRequest(updateRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Dictionary Extensions
|
||||
extension NSMutableDictionary {
|
||||
func setOptional(optional: AnyObject?, forKey key: NSCopying) {
|
||||
if let object: AnyObject = optional {
|
||||
self.setObject(object, forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// LocksmithRequest.swift
|
||||
// Locksmith-Demo
|
||||
//
|
||||
// Created by Matthew Palmer on 26/10/2014.
|
||||
// Copyright (c) 2014 Colour Coding. All rights reserved.
|
||||
//
|
||||
|
||||
enum SecurityClass: Int {
|
||||
case GenericPassword, InternetPassword, Certificate, Key, Identity
|
||||
}
|
||||
|
||||
import UIKit
|
||||
|
||||
enum MatchLimit: Int {
|
||||
case One, Many
|
||||
}
|
||||
|
||||
enum RequestType: Int {
|
||||
case Create, Read, Update, Delete
|
||||
}
|
||||
|
||||
class LocksmithRequest: NSObject, DebugPrintable {
|
||||
// Keychain Options
|
||||
// Required
|
||||
var service: String
|
||||
var key: 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 // Default to one
|
||||
|
||||
// Debugging
|
||||
override var debugDescription: String {
|
||||
|
||||
return "service: \(self.service), key: \(self.key), type: \(self.type.rawValue), userAccount: \(self.userAccount)"
|
||||
}
|
||||
|
||||
required init(service: String, userAccount: String, key: String) {
|
||||
self.service = service
|
||||
self.userAccount = userAccount
|
||||
self.key = key
|
||||
}
|
||||
|
||||
convenience init(service: String, userAccount: String, key: String, requestType: RequestType) {
|
||||
self.init(service: service, userAccount: userAccount, key: key)
|
||||
self.type = requestType
|
||||
}
|
||||
|
||||
convenience init(service: String, userAccount: String, key: String, requestType: RequestType, data: NSDictionary) {
|
||||
self.init(service: service, userAccount: userAccount, key: key, requestType: requestType)
|
||||
self.data = data
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// Locksmith
|
||||
//
|
||||
// Created by Matthew Palmer on 26/10/2014.
|
||||
// Copyright (c) 2014 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
let service = "Locksmith"
|
||||
let userAccount = "LocksmithUser"
|
||||
let key = "myKey"
|
||||
|
||||
@IBAction func save(sender: AnyObject) {
|
||||
Locksmith.saveData(["some key": "\(NSDate())"], forKey: key, inService: service, forUserAccount: userAccount)
|
||||
}
|
||||
|
||||
@IBAction func update(sender: AnyObject) {
|
||||
Locksmith.updateData(["some key": "\(NSDate())"], forKey: key, inService: service, forUserAccount: userAccount)
|
||||
}
|
||||
|
||||
@IBAction func loadData(sender: AnyObject) {
|
||||
let (dictionary, error) = Locksmith.loadData(forKey: key, inService: service, forUserAccount: userAccount)
|
||||
|
||||
if let dictionary = dictionary {
|
||||
println("Dictionary: \(dictionary)")
|
||||
}
|
||||
|
||||
if let error = error {
|
||||
println("Error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func deleteData(sender: AnyObject) {
|
||||
Locksmith.deleteData(forKey: key, inService: service, forUserAccount: userAccount)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// LocksmithTests.swift
|
||||
// LocksmithTests
|
||||
//
|
||||
// Created by Matthew Palmer on 26/10/2014.
|
||||
// Copyright (c) 2014 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
class LocksmithTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
XCTAssert(true, "Pass")
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
# Locksmith
|
||||
|
||||
> A sane way to work with the iOS Keychain in Swift.
|
||||
|
||||
# Usage
|
||||
|
||||
Grab two files from the project: Locksmith.swift and LocksmithRequest.swift.
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Save Data**
|
||||
|
||||
```swift
|
||||
Locksmith.saveData(["some key": "some value"], forKey: key, inService: service, forUserAccount: userAccount)
|
||||
```
|
||||
|
||||
**Load Data**
|
||||
|
||||
```swift
|
||||
let (dictionary, error) = Locksmith.loadData(forKey: key, inService: service, forUserAccount: userAccount)
|
||||
```
|
||||
|
||||
**Update Data**
|
||||
|
||||
```swift
|
||||
Locksmith.updateData(["some key": "another value"], forKey: key, inService: service, forUserAccount: userAccount)
|
||||
```
|
||||
|
||||
**Delete Data**
|
||||
```swift
|
||||
Locksmith.deleteData(forKey: key, inService: service, forUserAccount: userAccount)
|
||||
```
|
||||
|
||||
## Custom Requests
|
||||
To create custom keychain requests, you first have to instantiate a `LocksmithRequest`. This request can be customised as much as required. Then call`Locksmith.performRequest` on that request.
|
||||
|
||||
### Saving
|
||||
```swift
|
||||
let saveRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key, data: ["some key": "some value"])
|
||||
Locksmith.performRequest(saveRequest)
|
||||
```
|
||||
|
||||
### Reading
|
||||
```swift
|
||||
let readRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key)
|
||||
let (dictionary, error) = Locksmith.performRequest(readRequest)
|
||||
```
|
||||
|
||||
### Deleting
|
||||
```swift
|
||||
let deleteRequest = LocksmithRequest(service: service, userAccount: userAccount, key: key, requestType: .Delete)
|
||||
Locksmith.performRequest(deleteRequest)
|
||||
```
|
||||
|
||||
### `LocksmithRequest`
|
||||
*More to come.*
|
||||
|
||||
#### Required
|
||||
```swift
|
||||
var service: String
|
||||
var key: String
|
||||
var userAccount: String
|
||||
var type: RequestType // Defaults to .Read
|
||||
```
|
||||
|
||||
#### Optional
|
||||
```swift
|
||||
var group: String? // Used for keychain sharing
|
||||
var data: NSDictionary? // Used only for write requests
|
||||
var matchLimit: MatchLimit // Defaults to .One
|
||||
```
|
||||
@@ -0,0 +1,451 @@
|
||||
# Locksmith
|
||||
|
||||
A powerful, protocol-oriented library for working with the keychain in Swift.
|
||||
|
||||
- [x] 📱 iOS 8.0+
|
||||
- [x] 💻 Mac OS X 10.10+
|
||||
- [x] ⌚️ watchOS 2
|
||||
- [ ] 📺 tvOS (*coming soon?*)
|
||||
|
||||
How is Locksmith different to other keychain wrappers?
|
||||
|
||||
* Locksmith’s API is both super-simple and deeply powerful
|
||||
* Provides access to all of the keychain’s metadata with strongly typed results
|
||||
* Add functionality to your existing types for free
|
||||
* Useful enums and Swift-native types
|
||||
|
||||
> Want to read more about Locksmith’s design? I wrote a blog post on [protocol oriented programming in Swift](http://matthewpalmer.net/blog/2015/08/30/protocol-oriented-programming-in-the-real-world/).
|
||||
|
||||
## Installation
|
||||
|
||||
[](http://cocoadocs.org/docsets/Locksmith)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://travis-ci.org/matthewpalmer/Locksmith)
|
||||
|
||||
### CocoaPods
|
||||
|
||||
Locksmith is available through [CocoaPods](http://cocoapods.org).
|
||||
|
||||
pod 'Locksmith'
|
||||
|
||||
## Quick start
|
||||
|
||||
**Save data**
|
||||
|
||||
```swift
|
||||
try Locksmith.saveData(["some key": "some value"], forUserAccount: "myUserAccount")
|
||||
```
|
||||
|
||||
**Load data**
|
||||
|
||||
```swift
|
||||
let dictionary = Locksmith.loadDataForUserAccount("myUserAccount")
|
||||
```
|
||||
|
||||
**Update data**
|
||||
|
||||
- as well as replacing existing data, this writes data to the keychain if it does not exist already
|
||||
|
||||
```swift
|
||||
try Locksmith.updateData(["some key": "another value"], forUserAccount: "myUserAccount")
|
||||
```
|
||||
|
||||
**Delete data**
|
||||
|
||||
```swift
|
||||
try Locksmith.deleteDataForUserAccount("myUserAccount")
|
||||
```
|
||||
|
||||
## Power to the protocols
|
||||
|
||||
Locksmith has been designed with Swift 2, protocols, and protocol extensions in mind.
|
||||
|
||||
Why do this? Because you can add existing functionality to your types with only the slightest changes!
|
||||
|
||||
Say we have a Twitter account
|
||||
|
||||
```swift
|
||||
struct TwitterAccount {
|
||||
let username: String
|
||||
let password: String
|
||||
}
|
||||
```
|
||||
|
||||
and we want to save it to the keychain as a generic password. All we need to do is conform to the right protocols in Locksmith and we get that functionality for free.
|
||||
|
||||
```swift
|
||||
struct TwitterAccount: CreateableSecureStorable, GenericPasswordSecureStorable {
|
||||
let username: String
|
||||
let password: String
|
||||
|
||||
// Required by GenericPasswordSecureStorable
|
||||
let service = "Twitter"
|
||||
var account: String { return username }
|
||||
|
||||
// Required by CreateableSecureStorable
|
||||
var data: [String: AnyObject] {
|
||||
return ["password": password]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we get the ability to save our account in the keychain.
|
||||
|
||||
```swift
|
||||
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
|
||||
try account.createInSecureStore()
|
||||
```
|
||||
|
||||
Creating, reading, and deleting each have their own protocols: `CreateableSecureStorable`, `ReadableSecureStorable`, and `DeleteableSecureStorable`. And the best part?
|
||||
|
||||
**You can conform to all three protocols on the same type!**
|
||||
|
||||
```swift
|
||||
struct TwitterAccount: ReadableSecureStorable,
|
||||
CreateableSecureStorable,
|
||||
DeleteableSecureStorable,
|
||||
GenericPasswordSecureStorable {
|
||||
let username: String
|
||||
let password: String
|
||||
|
||||
let service = "Twitter"
|
||||
var account: String { return username }
|
||||
var data: [String: AnyObject] {
|
||||
return ["password": password]
|
||||
}
|
||||
}
|
||||
|
||||
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
|
||||
|
||||
// CreateableSecureStorable lets us create the account in the keychain
|
||||
try account.createInSecureStore()
|
||||
|
||||
// ReadableSecureStorable lets us read the account from the keychain
|
||||
let result = account.readFromSecureStore()
|
||||
|
||||
// DeleteableSecureStorable lets us delete the account from the keychain
|
||||
try account.deleteFromSecureStore()
|
||||
```
|
||||
|
||||
So. cool.
|
||||
|
||||
### The details
|
||||
|
||||
By declaring that your type adopts these protocols—which is what we did above with `struct TwitterAccount: CreateableSecureStorable, ...`—you get a bunch of functionality for free.
|
||||
|
||||
I like to think about protocols with extensions in terms of “what you get,” “what you’ve gotta do,” and “what’s optional.” Most of the stuff under ‘optional’ should only be implemented if you want to change existing functionality.
|
||||
|
||||
#### `CreateableSecureStorable`
|
||||
|
||||
**What you get**
|
||||
|
||||
```swift
|
||||
// Saves a type to the keychain
|
||||
func createInSecureStore() throws
|
||||
```
|
||||
|
||||
**Required**
|
||||
|
||||
```swift
|
||||
// The data to save to the keychain
|
||||
var data: [String: AnyObject] { get }
|
||||
```
|
||||
|
||||
**Optional**
|
||||
|
||||
```swift
|
||||
// Perform the request in this closure
|
||||
var performCreateRequestClosure: PerformRequestClosureType { get }
|
||||
```
|
||||
|
||||
#### `ReadableSecureStorable`
|
||||
|
||||
**What you get**
|
||||
|
||||
```swift
|
||||
// Read from the keychain
|
||||
func readFromSecureStore() -> SecureStorableResultType?
|
||||
```
|
||||
|
||||
**Required**
|
||||
|
||||
> Nothing!
|
||||
|
||||
**Optional**
|
||||
|
||||
```swift
|
||||
// Perform the request in this closure
|
||||
var performReadRequestClosure: PerformRequestClosureType { get }
|
||||
```
|
||||
|
||||
#### `DeleteableSecureStorable`
|
||||
|
||||
**What you get**
|
||||
|
||||
```swift
|
||||
// Read from the keychain
|
||||
func deleteFromSecureStore() throws
|
||||
```
|
||||
|
||||
**Required**
|
||||
|
||||
> Nothing!
|
||||
|
||||
**Optional**
|
||||
|
||||
```swift
|
||||
// Perform the request in this closure
|
||||
var performDeleteRequestClosure: PerformRequestClosureType { get }
|
||||
```
|
||||
|
||||
## Powerful support for the Cocoa Keychain
|
||||
|
||||
Many wrappers around the keychain have only support certain parts of the API. This is because there are so many options and variations on the way you can query the keychain that it’s almost impossible to abstract effectively.
|
||||
|
||||
Locksmith tries to include as much of the keychain as possible, using protocols and protocol extensions to minimize the complexity. You can mix-and-match your generic passwords with your read requests while staying completely type-safe.
|
||||
|
||||
Please refer to the [Keychain Services Reference](https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/) for full information on what each of the attributes mean and what they can do.
|
||||
|
||||
> Certificates, keys, and identities are possible—it’s just a matter of translating the `kSec...` constants!
|
||||
|
||||
#### `GenericPasswordSecureStorable`
|
||||
|
||||
Generic passwords are probably the most common use-case of the keychain, and are great for storing usernames and passwords.
|
||||
|
||||
Properties listed under ‘Required’ have to be implemented by any types that conform; those listed under ‘Optional’ can be implemented to add additional information to what is saved or read if desired.
|
||||
|
||||
One thing to note: if you implement an optional property, its type annotation must match the type specified in the protocol *exactly*. If you implement `description: String?` it can’t be declared as `var description: String`.
|
||||
|
||||
**Required**
|
||||
|
||||
```swift
|
||||
var account: String { get }
|
||||
var service: String { get }
|
||||
```
|
||||
|
||||
**Optional**
|
||||
|
||||
```swift
|
||||
var comment: String? { get }
|
||||
var creator: UInt? { get }
|
||||
var description: String? { get }
|
||||
var generic: NSData? { get }
|
||||
var isInvisible: Bool? { get }
|
||||
var isNegative: Bool? { get }
|
||||
var label: String? { get }
|
||||
var type: UInt? { get }
|
||||
```
|
||||
|
||||
#### `InternetPasswordSecureStorable`
|
||||
|
||||
Types that conform to `InternetPasswordSecureStorable` typically come from web services and have certain associated metadata.
|
||||
|
||||
**Required**
|
||||
|
||||
```swift
|
||||
var account: String { get }
|
||||
var authenticationType: LocksmithInternetAuthenticationType { get }
|
||||
var internetProtocol: LocksmithInternetProtocol { get }
|
||||
var port: String { get }
|
||||
var server: String { get }
|
||||
```
|
||||
|
||||
**Optional**
|
||||
|
||||
```swift
|
||||
var comment: String? { get }
|
||||
var creator: UInt? { get }
|
||||
var description: String? { get }
|
||||
var isInvisible: Bool? { get }
|
||||
var isNegative: Bool? { get }
|
||||
var path: String? { get }
|
||||
var securityDomain: String? { get }
|
||||
var type: UInt? { get }
|
||||
```
|
||||
|
||||
## Result types
|
||||
|
||||
By adopting a protocol-oriented design from the ground up, Locksmith can provide access to the result of your keychain queries *with type annotations included*—store an `NSDate`, get an `NSDate` back with no type-casting!
|
||||
|
||||
Let’s start with an example: the Twitter account from before, except it’s now an `InternetPasswordSecureStorable`, which lets us store a bit more metadata.
|
||||
|
||||
```swift
|
||||
struct TwitterAccount: InternetPasswordSecureStorable,
|
||||
ReadableSecureStorable,
|
||||
CreateableSecureStorable {
|
||||
let username: String
|
||||
let password: String
|
||||
|
||||
var account: String { return username }
|
||||
var data: [String: AnyObject] {
|
||||
return ["password": password]
|
||||
}
|
||||
|
||||
let server = "com.twitter"
|
||||
let port = 80
|
||||
let internetProtocol = .HTTPS
|
||||
let authenticationType = .HTTPBasic
|
||||
let path: String? = "/api/2.0/"
|
||||
}
|
||||
|
||||
let account = TwitterAccount(username: "_matthewpalmer", password: "my_password")
|
||||
|
||||
// Save all this to the keychain
|
||||
account.createInSecureStore()
|
||||
|
||||
// Now let’s get it back
|
||||
let result: InternetPasswordSecureStorableResultType = account.readFromSecureStore()
|
||||
|
||||
result?.port // Gives us an Int directly!
|
||||
result?.internetProtocol // Gives us a LocksmithInternetProtocol enum case directly!
|
||||
result?.data // Gives us a [String: AnyObject] of what was saved
|
||||
// and so on...
|
||||
```
|
||||
|
||||
This is *awesome*. No more typecasting.
|
||||
|
||||
#### `GenericPasswordSecureStorableResultType`
|
||||
|
||||
Everything listed here can be set on a type conforming to `GenericPasswordSecureStorable`, and gotten back from the result returned from `readFromSecureStore()` on that type.
|
||||
|
||||
```swift
|
||||
var account: String { get }
|
||||
var service: String { get }
|
||||
var comment: String? { get }
|
||||
var creator: UInt? { get }
|
||||
var description: String? { get }
|
||||
var data: [String: AnyObject]? { get }
|
||||
var generic: NSData? { get }
|
||||
var isInvisible: Bool? { get }
|
||||
var isNegative: Bool? { get }
|
||||
var label: String? { get }
|
||||
var type: UInt? { get }
|
||||
```
|
||||
|
||||
#### `InternetPasswordSecureStorableResultType`
|
||||
|
||||
Everything listed here can be set on a type conforming to `InternetPasswordSecureStorable`, and gotten back from the result returned from `readFromSecureStore()` on that type.
|
||||
|
||||
```swift
|
||||
var account: String { get }
|
||||
var authenticationType: LocksmithInternetAuthenticationType { get }
|
||||
var internetProtocol: LocksmithInternetProtocol { get }
|
||||
var port: Int { get }
|
||||
var server: String { get }
|
||||
var comment: String? { get }
|
||||
var creator: UInt? { get }
|
||||
var data: [String: AnyObject]? { get }
|
||||
var description: String? { get }
|
||||
var isInvisible: Bool? { get }
|
||||
var isNegative: Bool? { get }
|
||||
var path: String? { get }
|
||||
var securityDomain: String? { get }
|
||||
var type: UInt? { get }
|
||||
```
|
||||
|
||||
## Enumerations
|
||||
|
||||
Locksmith provides a bunch of handy enums for configuring your requests, so you can say `kSecGoodByeStringConstants`.
|
||||
|
||||
#### `LocksmithAccessibleOption`
|
||||
|
||||
`LocksmithAccessibleOption` configures when an item can be accessed—you might require that stuff is available when the device is unlocked, after a passcode has been entered, etc.
|
||||
|
||||
```swift
|
||||
public enum LocksmithAccessibleOption {
|
||||
case AfterFirstUnlock
|
||||
case AfterFirstUnlockThisDeviceOnly
|
||||
case Always
|
||||
case AlwaysThisDeviceOnly
|
||||
case WhenPasscodeSetThisDeviceOnly
|
||||
case WhenUnlocked
|
||||
case WhenUnlockedThisDeviceOnly
|
||||
}
|
||||
```
|
||||
|
||||
#### `LocksmithError`
|
||||
|
||||
`LocksmithError` provides Swift-friendly translations of common keychain error codes. These are thrown from methods throughout the library.
|
||||
|
||||
```swift
|
||||
public enum LocksmithError: ErrorType {
|
||||
case Allocate
|
||||
case AuthFailed
|
||||
case Decode
|
||||
case Duplicate
|
||||
case InteractionNotAllowed
|
||||
case NoError
|
||||
case NotAvailable
|
||||
case NotFound
|
||||
case Param
|
||||
case RequestNotSet
|
||||
case TypeNotFound
|
||||
case UnableToClear
|
||||
case Undefined
|
||||
case Unimplemented
|
||||
}
|
||||
```
|
||||
|
||||
#### `LocksmithInternetAuthenticationType`
|
||||
|
||||
`LocksmithInternetAuthenticationType` lets you pick out the type of authentication you want to store alongside your `.InternetPassword`s—anything from `.MSN` to `.HTTPDigest`.
|
||||
|
||||
```swift
|
||||
public enum LocksmithInternetAuthenticationType {
|
||||
case Default
|
||||
case DPA
|
||||
case HTMLForm
|
||||
case HTTPBasic
|
||||
case HTTPDigest
|
||||
case MSN
|
||||
case NTLM
|
||||
case RPA
|
||||
}
|
||||
```
|
||||
|
||||
#### `LocksmithInternetProtocol`
|
||||
|
||||
`LocksmithInternetProtocol` is used with `.InternetPassword` to choose which protocol was used for the interaction with the web service, including `.HTTP`, `.SMB`, and a whole bunch more.
|
||||
|
||||
```swift
|
||||
public enum {
|
||||
case AFP
|
||||
case AppleTalk
|
||||
case DAAP
|
||||
case EPPC
|
||||
case FTP
|
||||
case FTPAccount
|
||||
case FTPProxy
|
||||
case FTPS
|
||||
case HTTP
|
||||
case HTTPProxy
|
||||
case HTTPS
|
||||
case HTTPSProxy
|
||||
case IMAP
|
||||
case IMAPS
|
||||
case IPP
|
||||
case IRC
|
||||
case IRCS
|
||||
case LDAP
|
||||
case NNTP
|
||||
case NNTPS, LDAPS
|
||||
case POP3
|
||||
case POP3S
|
||||
case RTSP
|
||||
case RTSPProxy
|
||||
case SMB
|
||||
case SMTP
|
||||
case SOCKS
|
||||
case SSH
|
||||
case Telnet
|
||||
case TelnetS
|
||||
}
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
[Matthew Palmer](http://matthewpalmer.net), matt@matthewpalmer.net
|
||||
|
||||
## License
|
||||
|
||||
Locksmith is available under the MIT license. See the LICENSE file for more info.
|
||||
@@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
public extension Dictionary {
|
||||
init(withoutOptionalValues initial: Dictionary<Key, Value?>) {
|
||||
self = [Key: Value]()
|
||||
for pair in initial {
|
||||
if pair.1 != nil {
|
||||
self[pair.0] = pair.1!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(pairs: [(Key, Value)]) {
|
||||
self = [Key: Value]()
|
||||
pairs.forEach { (k, v) -> () in
|
||||
self[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
init(initial: Dictionary<Key, Value>, toMerge: Dictionary<Key, Value>) {
|
||||
self = Dictionary<Key, Value>()
|
||||
|
||||
for pair in initial {
|
||||
self[pair.0] = pair.1
|
||||
}
|
||||
|
||||
for pair in toMerge {
|
||||
self[pair.0] = pair.1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.matthewpalmer.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,598 @@
|
||||
import Foundation
|
||||
|
||||
public let LocksmithDefaultService = NSBundle.mainBundle().infoDictionary![String(kCFBundleIdentifierKey)] as? String ?? "com.locksmith.defaultService"
|
||||
|
||||
public typealias PerformRequestClosureType = (requestReference: CFDictionaryRef, inout result: AnyObject?) -> (OSStatus)
|
||||
|
||||
|
||||
// MARK: - Locksmith
|
||||
public struct Locksmith {
|
||||
public static func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> [String: AnyObject]? {
|
||||
struct ReadRequest: GenericPasswordSecureStorable, ReadableSecureStorable {
|
||||
let service: String
|
||||
let account: String
|
||||
}
|
||||
|
||||
let request = ReadRequest(service: service, account: userAccount)
|
||||
return request.readFromSecureStore()?.data
|
||||
}
|
||||
|
||||
public static func saveData(data: [String: AnyObject], forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
|
||||
struct CreateRequest: GenericPasswordSecureStorable, CreateableSecureStorable {
|
||||
let service: String
|
||||
let account: String
|
||||
let data: [String: AnyObject]
|
||||
}
|
||||
|
||||
let request = CreateRequest(service: service, account: userAccount, data: data)
|
||||
return try request.createInSecureStore()
|
||||
}
|
||||
|
||||
public static func deleteDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) throws {
|
||||
struct DeleteRequest: GenericPasswordSecureStorable, DeleteableSecureStorable {
|
||||
let service: String
|
||||
let account: String
|
||||
}
|
||||
|
||||
let request = DeleteRequest(service: service, account: userAccount)
|
||||
return try request.deleteFromSecureStore()
|
||||
}
|
||||
|
||||
public static func updateData(data: [String: AnyObject], forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
|
||||
// Delete and then re-save
|
||||
do {
|
||||
try Locksmith.deleteDataForUserAccount(userAccount, inService: service)
|
||||
} catch {
|
||||
// Deletion is likely to fail if the piece of data doesn't exist yet.
|
||||
// This doesn't matter--we only tell the user about errors on the save request.
|
||||
}
|
||||
|
||||
return try Locksmith.saveData(data, forUserAccount: userAccount, inService: service)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SecureStorable
|
||||
/// The base protocol that indicates conforming types will have the ability to be stored in a secure storage container, such as the iOS keychain.
|
||||
public protocol SecureStorable {
|
||||
var accessible: LocksmithAccessibleOption? { get }
|
||||
var accessGroup: String? { get }
|
||||
}
|
||||
|
||||
public extension SecureStorable {
|
||||
var accessible: LocksmithAccessibleOption? { return nil }
|
||||
var accessGroup: String? { return nil }
|
||||
|
||||
var secureStorableBaseStoragePropertyDictionary: [String: AnyObject] {
|
||||
let dictionary = [
|
||||
String(kSecAttrAccessGroup): accessGroup,
|
||||
String(kSecAttrAccessible): accessible?.rawValue
|
||||
]
|
||||
|
||||
return Dictionary(withoutOptionalValues: dictionary)
|
||||
}
|
||||
|
||||
private func performSecureStorageAction(closure: PerformRequestClosureType, secureStoragePropertyDictionary: [String: AnyObject]) throws -> [String: AnyObject]? {
|
||||
var result: AnyObject?
|
||||
let request = secureStoragePropertyDictionary
|
||||
let requestReference = request as CFDictionaryRef
|
||||
|
||||
let status = closure(requestReference: requestReference, result: &result)
|
||||
|
||||
let statusCode = Int(status)
|
||||
|
||||
if let error = LocksmithError(fromStatusCode: statusCode) {
|
||||
throw error
|
||||
}
|
||||
|
||||
// hmmmm... bit leaky
|
||||
if status != errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let dictionary = result as? NSDictionary else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dictionary[String(kSecValueData)] as? NSData == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result as? [String: AnyObject]
|
||||
}
|
||||
}
|
||||
|
||||
public extension SecureStorable where Self : InternetPasswordSecureStorable {
|
||||
private var internetPasswordBaseStoragePropertyDictionary: [String: AnyObject] {
|
||||
var dictionary = [String: AnyObject]()
|
||||
|
||||
// add in whatever turns out to be required...
|
||||
dictionary[String(kSecAttrServer)] = server
|
||||
dictionary[String(kSecAttrPort)] = port
|
||||
dictionary[String(kSecAttrProtocol)] = internetProtocol.rawValue
|
||||
dictionary[String(kSecAttrAuthenticationType)] = authenticationType.rawValue
|
||||
dictionary[String(kSecAttrSecurityDomain)] = securityDomain
|
||||
dictionary[String(kSecAttrPath)] = path
|
||||
dictionary[String(kSecClass)] = LocksmithSecurityClass.InternetPassword.rawValue
|
||||
|
||||
let toMergeWith = [
|
||||
accountSecureStoragePropertyDictionary,
|
||||
describableSecureStoragePropertyDictionary,
|
||||
commentableSecureStoragePropertyDictionary,
|
||||
creatorDesignatableSecureStoragePropertyDictionary,
|
||||
typeDesignatableSecureStoragePropertyDictionary,
|
||||
isInvisibleSecureStoragePropertyDictionary,
|
||||
isNegativeSecureStoragePropertyDictionary
|
||||
]
|
||||
|
||||
for dict in toMergeWith {
|
||||
dictionary = Dictionary(initial: dictionary, toMerge: dict)
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
|
||||
public protocol AccountBasedSecureStorable {
|
||||
/// The account that the stored value will belong to
|
||||
var account: String { get }
|
||||
}
|
||||
|
||||
public extension AccountBasedSecureStorable {
|
||||
private var accountSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return [String(kSecAttrAccount): account]
|
||||
}
|
||||
}
|
||||
|
||||
public protocol AccountBasedSecureStorableResultType: AccountBasedSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension AccountBasedSecureStorableResultType {
|
||||
var account: String {
|
||||
return resultDictionary[String(kSecAttrAccount)] as! String
|
||||
}
|
||||
}
|
||||
|
||||
public protocol DescribableSecureStorable {
|
||||
/// A description of the item in the secure storage container.
|
||||
var description: String? { get }
|
||||
}
|
||||
|
||||
public extension DescribableSecureStorable {
|
||||
var description: String? { return nil }
|
||||
|
||||
private var describableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [
|
||||
String(kSecAttrDescription): description
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol DescribableSecureStorableResultType: DescribableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension DescribableSecureStorableResultType {
|
||||
var description: String? {
|
||||
return resultDictionary[String(kSecAttrDescription)] as? String
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CommentableSecureStorable {
|
||||
/// A comment attached to the item in the secure storage container.
|
||||
var comment: String? { get }
|
||||
}
|
||||
|
||||
public extension CommentableSecureStorable {
|
||||
var comment: String? { return nil }
|
||||
|
||||
private var commentableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [
|
||||
String(kSecAttrComment): comment
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CommentableSecureStorableResultType: CommentableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension CommentableSecureStorableResultType {
|
||||
var comment: String? {
|
||||
return resultDictionary[String(kSecAttrComment)] as? String
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CreatorDesignatableSecureStorable {
|
||||
/// The creator of the item in the secure storage container.
|
||||
var creator: UInt? { get }
|
||||
}
|
||||
|
||||
public extension CreatorDesignatableSecureStorable {
|
||||
var creator: UInt? { return nil }
|
||||
|
||||
private var creatorDesignatableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [String(kSecAttrCreator): creator])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CreatorDesignatableSecureStorableResultType: CreatorDesignatableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension CreatorDesignatableSecureStorableResultType {
|
||||
var creator: UInt? {
|
||||
return resultDictionary[String(kSecAttrCreator)] as? UInt
|
||||
}
|
||||
}
|
||||
|
||||
public protocol LabellableSecureStorable {
|
||||
/// A label for the item in the secure storage container.
|
||||
var label: String? { get }
|
||||
}
|
||||
|
||||
public extension LabellableSecureStorable {
|
||||
var label: String? { return nil }
|
||||
|
||||
private var labellableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [String(kSecAttrLabel): label])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol LabellableSecureStorableResultType: LabellableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension LabellableSecureStorableResultType {
|
||||
var label: String? {
|
||||
return resultDictionary[String(kSecAttrLabel)] as? String
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TypeDesignatableSecureStorable {
|
||||
/// The type of the stored item
|
||||
var type: UInt? { get }
|
||||
}
|
||||
|
||||
public extension TypeDesignatableSecureStorable {
|
||||
var type: UInt? { return nil }
|
||||
|
||||
private var typeDesignatableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [String(kSecAttrType): type])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TypeDesignatableSecureStorableResultType: TypeDesignatableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension TypeDesignatableSecureStorableResultType {
|
||||
var type: UInt? {
|
||||
return resultDictionary[String(kSecAttrType)] as? UInt
|
||||
}
|
||||
}
|
||||
|
||||
public protocol IsInvisibleAssignableSecureStorable {
|
||||
var isInvisible: Bool? { get }
|
||||
}
|
||||
|
||||
public extension IsInvisibleAssignableSecureStorable {
|
||||
var isInvisible: Bool? { return nil }
|
||||
|
||||
private var isInvisibleSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [String(kSecAttrIsInvisible): isInvisible])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol IsInvisibleAssignableSecureStorableResultType: IsInvisibleAssignableSecureStorable, SecureStorableResultType {}
|
||||
|
||||
public extension IsInvisibleAssignableSecureStorableResultType {
|
||||
var isInvisible: Bool? {
|
||||
return resultDictionary[String(kSecAttrIsInvisible)] as? Bool
|
||||
}
|
||||
}
|
||||
|
||||
public protocol IsNegativeAssignableSecureStorable {
|
||||
var isNegative: Bool? { get }
|
||||
}
|
||||
|
||||
public extension IsNegativeAssignableSecureStorable {
|
||||
var isNegative: Bool? { return nil }
|
||||
|
||||
private var isNegativeSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return Dictionary(withoutOptionalValues: [String(kSecAttrIsNegative): isNegative])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol IsNegativeAssignableSecureStorableResultType: IsNegativeAssignableSecureStorable, SecureStorableResultType {
|
||||
}
|
||||
|
||||
public extension IsNegativeAssignableSecureStorableResultType {
|
||||
var isNegative: Bool? {
|
||||
return resultDictionary[String(kSecAttrIsNegative)] as? Bool
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - GenericPasswordSecureStorable
|
||||
/// The protocol that indicates a type conforms to the requirements of a generic password item in a secure storage container.
|
||||
/// Generic passwords are the most common types of things that are stored securely.
|
||||
public protocol GenericPasswordSecureStorable: AccountBasedSecureStorable, DescribableSecureStorable, CommentableSecureStorable, CreatorDesignatableSecureStorable, LabellableSecureStorable, TypeDesignatableSecureStorable, IsInvisibleAssignableSecureStorable, IsNegativeAssignableSecureStorable {
|
||||
|
||||
/// The service to which the type belongs
|
||||
var service: String { get }
|
||||
|
||||
// Optional properties
|
||||
var generic: NSData? { get }
|
||||
}
|
||||
|
||||
// Add extension to allow for optional properties in protocol
|
||||
public extension GenericPasswordSecureStorable {
|
||||
var generic: NSData? { return nil}
|
||||
}
|
||||
|
||||
// dear god what have i done...
|
||||
public protocol GenericPasswordSecureStorableResultType: GenericPasswordSecureStorable, SecureStorableResultType, AccountBasedSecureStorableResultType, DescribableSecureStorableResultType, CommentableSecureStorableResultType, CreatorDesignatableSecureStorableResultType, LabellableSecureStorableResultType, TypeDesignatableSecureStorableResultType, IsInvisibleAssignableSecureStorableResultType, IsNegativeAssignableSecureStorableResultType {}
|
||||
|
||||
public extension GenericPasswordSecureStorableResultType {
|
||||
var service: String {
|
||||
return resultDictionary[String(kSecAttrService)] as! String
|
||||
}
|
||||
|
||||
var generic: NSData? {
|
||||
return resultDictionary[String(kSecAttrGeneric)] as? NSData
|
||||
}
|
||||
}
|
||||
|
||||
public extension SecureStorable where Self : GenericPasswordSecureStorable {
|
||||
private var genericPasswordBaseStoragePropertyDictionary: [String: AnyObject] {
|
||||
var dictionary = [String: AnyObject?]()
|
||||
|
||||
dictionary[String(kSecAttrService)] = service
|
||||
dictionary[String(kSecAttrGeneric)] = generic
|
||||
dictionary[String(kSecClass)] = LocksmithSecurityClass.GenericPassword.rawValue
|
||||
|
||||
dictionary = Dictionary(initial: dictionary, toMerge: describableSecureStoragePropertyDictionary)
|
||||
|
||||
let toMergeWith = [
|
||||
secureStorableBaseStoragePropertyDictionary,
|
||||
accountSecureStoragePropertyDictionary,
|
||||
describableSecureStoragePropertyDictionary,
|
||||
commentableSecureStoragePropertyDictionary,
|
||||
creatorDesignatableSecureStoragePropertyDictionary,
|
||||
typeDesignatableSecureStoragePropertyDictionary,
|
||||
labellableSecureStoragePropertyDictionary,
|
||||
isInvisibleSecureStoragePropertyDictionary,
|
||||
isNegativeSecureStoragePropertyDictionary
|
||||
]
|
||||
|
||||
for dict in toMergeWith {
|
||||
dictionary = Dictionary(initial: dictionary, toMerge: dict)
|
||||
}
|
||||
|
||||
return Dictionary(withoutOptionalValues: dictionary)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - InternetPasswordSecureStorable
|
||||
/// A protocol that indicates a type conforms to the requirements of an internet password in a secure storage container.
|
||||
public protocol InternetPasswordSecureStorable: AccountBasedSecureStorable, DescribableSecureStorable, CommentableSecureStorable, CreatorDesignatableSecureStorable, TypeDesignatableSecureStorable, IsInvisibleAssignableSecureStorable, IsNegativeAssignableSecureStorable {
|
||||
var server: String { get }
|
||||
var port: Int { get }
|
||||
var internetProtocol: LocksmithInternetProtocol { get }
|
||||
var authenticationType: LocksmithInternetAuthenticationType { get }
|
||||
var securityDomain: String? { get }
|
||||
var path: String? { get }
|
||||
}
|
||||
|
||||
public extension InternetPasswordSecureStorable {
|
||||
var securityDomain: String? { return nil }
|
||||
var path: String? { return nil }
|
||||
}
|
||||
|
||||
public protocol InternetPasswordSecureStorableResultType: AccountBasedSecureStorableResultType, DescribableSecureStorableResultType, CommentableSecureStorableResultType, CreatorDesignatableSecureStorableResultType, TypeDesignatableSecureStorableResultType, IsInvisibleAssignableSecureStorableResultType, IsNegativeAssignableSecureStorableResultType {}
|
||||
|
||||
public extension InternetPasswordSecureStorableResultType {
|
||||
private func stringFromResultDictionary(key: CFString) -> String? {
|
||||
return resultDictionary[String(key)] as? String
|
||||
}
|
||||
|
||||
var server: String {
|
||||
return stringFromResultDictionary(kSecAttrServer)!
|
||||
}
|
||||
|
||||
var port: Int {
|
||||
return resultDictionary[String(kSecAttrPort)] as! Int
|
||||
}
|
||||
|
||||
var internetProtocol: LocksmithInternetProtocol {
|
||||
return LocksmithInternetProtocol(rawValue: stringFromResultDictionary(kSecAttrProtocol)!)!
|
||||
}
|
||||
|
||||
var authenticationType: LocksmithInternetAuthenticationType {
|
||||
return LocksmithInternetAuthenticationType(rawValue: stringFromResultDictionary(kSecAttrAuthenticationType)!)!
|
||||
}
|
||||
|
||||
var securityDomain: String? {
|
||||
return stringFromResultDictionary(kSecAttrSecurityDomain)
|
||||
}
|
||||
|
||||
var path: String? {
|
||||
return stringFromResultDictionary(kSecAttrPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CertificateSecureStorable
|
||||
|
||||
public protocol CertificateSecureStorable: SecureStorable {}
|
||||
|
||||
// MARK: - KeySecureStorable
|
||||
|
||||
public protocol KeySecureStorable: SecureStorable {}
|
||||
|
||||
// MARK: - CreateableSecureStorable
|
||||
|
||||
/// Conformance to this protocol indicates that your type is able to be created and saved to a secure storage container.
|
||||
public protocol CreateableSecureStorable: SecureStorable {
|
||||
var data: [String: AnyObject] { get }
|
||||
var performCreateRequestClosure: PerformRequestClosureType { get }
|
||||
func createInSecureStore() throws
|
||||
}
|
||||
|
||||
// MARK: - ReadableSecureStorable
|
||||
/// Conformance to this protocol indicates that your type is able to be read from a secure storage container.
|
||||
public protocol ReadableSecureStorable: SecureStorable {
|
||||
var performReadRequestClosure: PerformRequestClosureType { get }
|
||||
func readFromSecureStore() -> SecureStorableResultType?
|
||||
}
|
||||
|
||||
public extension ReadableSecureStorable {
|
||||
var performReadRequestClosure: PerformRequestClosureType {
|
||||
return { (requestReference: CFDictionaryRef, inout result: AnyObject?) in
|
||||
return withUnsafeMutablePointer(&result) { SecItemCopyMatching(requestReference, UnsafeMutablePointer($0)) }
|
||||
}
|
||||
}
|
||||
|
||||
func readFromSecureStore() -> SecureStorableResultType? {
|
||||
// This must be implemented here so that we can properly override it in the type-specific implementations
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public extension ReadableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
var asReadableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
var old = genericPasswordBaseStoragePropertyDictionary
|
||||
old[String(kSecReturnData)] = true
|
||||
old[String(kSecMatchLimit)] = kSecMatchLimitOne
|
||||
old[String(kSecReturnAttributes)] = kCFBooleanTrue
|
||||
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
public extension ReadableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
var asReadableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
var old = internetPasswordBaseStoragePropertyDictionary
|
||||
old[String(kSecReturnData)] = true
|
||||
old[String(kSecMatchLimit)] = kSecMatchLimitOne
|
||||
old[String(kSecReturnAttributes)] = kCFBooleanTrue
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
struct GenericPasswordResult: GenericPasswordSecureStorableResultType {
|
||||
var resultDictionary: [String: AnyObject]
|
||||
}
|
||||
|
||||
public extension ReadableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
func readFromSecureStore() -> GenericPasswordSecureStorableResultType? {
|
||||
do {
|
||||
let result = try performSecureStorageAction(performReadRequestClosure, secureStoragePropertyDictionary: asReadableSecureStoragePropertyDictionary)
|
||||
return GenericPasswordResult(resultDictionary: result!)
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension ReadableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
func readFromSecureStore() -> InternetPasswordSecureStorableResultType? {
|
||||
do {
|
||||
let result = try performSecureStorageAction(performReadRequestClosure, secureStoragePropertyDictionary: asReadableSecureStoragePropertyDictionary)
|
||||
return InternetPasswordResult(resultDictionary: result!)
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DeleteableSecureStorable
|
||||
/// Conformance to this protocol indicates that your type is able to be deleted from a secure storage container.
|
||||
public protocol DeleteableSecureStorable: SecureStorable {
|
||||
var performDeleteRequestClosure: PerformRequestClosureType { get }
|
||||
func deleteFromSecureStore() throws
|
||||
}
|
||||
|
||||
// MARK: - Default property dictionaries
|
||||
|
||||
public extension CreateableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
var asCreateableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
var old = genericPasswordBaseStoragePropertyDictionary
|
||||
old[String(kSecValueData)] = NSKeyedArchiver.archivedDataWithRootObject(data)
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
public extension CreateableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
func createInSecureStore() throws {
|
||||
try performSecureStorageAction(performCreateRequestClosure, secureStoragePropertyDictionary: asCreateableSecureStoragePropertyDictionary)
|
||||
}
|
||||
}
|
||||
|
||||
public extension CreateableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
var asCreateableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
var old = internetPasswordBaseStoragePropertyDictionary
|
||||
old[String(kSecValueData)] = NSKeyedArchiver.archivedDataWithRootObject(data)
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
public extension CreateableSecureStorable {
|
||||
var performCreateRequestClosure: PerformRequestClosureType {
|
||||
return { (requestReference: CFDictionaryRef, inout result: AnyObject?) in
|
||||
return withUnsafeMutablePointer(&result) { SecItemAdd(requestReference, UnsafeMutablePointer($0)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension CreateableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
func createInSecureStore() throws {
|
||||
try performSecureStorageAction(performCreateRequestClosure, secureStoragePropertyDictionary: asCreateableSecureStoragePropertyDictionary)
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeleteableSecureStorable {
|
||||
var performDeleteRequestClosure: PerformRequestClosureType {
|
||||
return { (requestReference, _) in
|
||||
return SecItemDelete(requestReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeleteableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
var asDeleteableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return genericPasswordBaseStoragePropertyDictionary
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeleteableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
var asDeleteableSecureStoragePropertyDictionary: [String: AnyObject] {
|
||||
return internetPasswordBaseStoragePropertyDictionary
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeleteableSecureStorable where Self : GenericPasswordSecureStorable {
|
||||
func deleteFromSecureStore() throws {
|
||||
try performSecureStorageAction(performDeleteRequestClosure, secureStoragePropertyDictionary: asDeleteableSecureStoragePropertyDictionary)
|
||||
}
|
||||
}
|
||||
|
||||
public extension DeleteableSecureStorable where Self : InternetPasswordSecureStorable {
|
||||
func deleteFromSecureStore() throws {
|
||||
try performSecureStorageAction(performDeleteRequestClosure, secureStoragePropertyDictionary: asDeleteableSecureStoragePropertyDictionary)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: ResultTypes
|
||||
public protocol SecureStorableResultType: SecureStorable {
|
||||
var resultDictionary: [String: AnyObject] { get }
|
||||
var data: [String: AnyObject]? { get }
|
||||
}
|
||||
|
||||
struct InternetPasswordResult: InternetPasswordSecureStorableResultType {
|
||||
var resultDictionary: [String: AnyObject]
|
||||
}
|
||||
|
||||
public extension SecureStorableResultType {
|
||||
var resultDictionary: [String: AnyObject] {
|
||||
return [String: AnyObject]()
|
||||
}
|
||||
|
||||
var data: [String: AnyObject]? {
|
||||
guard let aData = resultDictionary[String(kSecValueData)] as? NSData else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSKeyedUnarchiver.unarchiveObjectWithData(aData) as? [String: AnyObject]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
|
||||
// MARK: Accessible
|
||||
public enum LocksmithAccessibleOption: RawRepresentable {
|
||||
case WhenUnlocked, AfterFirstUnlock, Always, WhenUnlockedThisDeviceOnly, AfterFirstUnlockThisDeviceOnly, AlwaysThisDeviceOnly, WhenPasscodeSetThisDeviceOnly
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case String(kSecAttrAccessibleWhenUnlocked):
|
||||
self = WhenUnlocked
|
||||
case String(kSecAttrAccessibleAfterFirstUnlock):
|
||||
self = AfterFirstUnlock
|
||||
case String(kSecAttrAccessibleAlways):
|
||||
self = Always
|
||||
case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly):
|
||||
self = WhenUnlockedThisDeviceOnly
|
||||
case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly):
|
||||
self = AfterFirstUnlockThisDeviceOnly
|
||||
case String(kSecAttrAccessibleAlwaysThisDeviceOnly):
|
||||
self = AlwaysThisDeviceOnly
|
||||
case String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly):
|
||||
self = WhenPasscodeSetThisDeviceOnly
|
||||
default:
|
||||
self = WhenUnlocked
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .WhenUnlocked:
|
||||
return String(kSecAttrAccessibleWhenUnlocked)
|
||||
case .AfterFirstUnlock:
|
||||
return String(kSecAttrAccessibleAfterFirstUnlock)
|
||||
case .Always:
|
||||
return String(kSecAttrAccessibleAlways)
|
||||
case .WhenPasscodeSetThisDeviceOnly:
|
||||
return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
|
||||
case .WhenUnlockedThisDeviceOnly:
|
||||
return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
|
||||
case .AfterFirstUnlockThisDeviceOnly:
|
||||
return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
|
||||
case .AlwaysThisDeviceOnly:
|
||||
return String(kSecAttrAccessibleAlwaysThisDeviceOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import Foundation
|
||||
|
||||
// MARK: Locksmith Error
|
||||
public enum LocksmithError: String, ErrorType {
|
||||
case Allocate = "Failed to allocate memory."
|
||||
case AuthFailed = "Authorization/Authentication failed."
|
||||
case Decode = "Unable to decode the provided data."
|
||||
case Duplicate = "The item already exists."
|
||||
case InteractionNotAllowed = "Interaction with the Security Server is not allowed."
|
||||
case NoError = "No error."
|
||||
case NotAvailable = "No trust results are available."
|
||||
case NotFound = "The item cannot be found."
|
||||
case Param = "One or more parameters passed to the function were not valid."
|
||||
case RequestNotSet = "The request was not set"
|
||||
case TypeNotFound = "The type was not found"
|
||||
case UnableToClear = "Unable to clear the keychain"
|
||||
case Undefined = "An undefined error occurred"
|
||||
case Unimplemented = "Function or operation not implemented."
|
||||
|
||||
init?(fromStatusCode code: Int) {
|
||||
switch code {
|
||||
case Int(errSecAllocate):
|
||||
self = Allocate
|
||||
case Int(errSecAuthFailed):
|
||||
self = AuthFailed
|
||||
case Int(errSecDecode):
|
||||
self = Decode
|
||||
case Int(errSecDuplicateItem):
|
||||
self = Duplicate
|
||||
case Int(errSecInteractionNotAllowed):
|
||||
self = InteractionNotAllowed
|
||||
case Int(errSecItemNotFound):
|
||||
self = NotFound
|
||||
case Int(errSecNotAvailable):
|
||||
self = NotAvailable
|
||||
case Int(errSecParam):
|
||||
self = Param
|
||||
case Int(errSecUnimplemented):
|
||||
self = Unimplemented
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Foundation
|
||||
|
||||
public enum LocksmithInternetAuthenticationType: RawRepresentable {
|
||||
case NTLM, MSN, DPA, RPA, HTTPBasic, HTTPDigest, HTMLForm, Default
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case String(kSecAttrAuthenticationTypeNTLM):
|
||||
self = NTLM
|
||||
case String(kSecAttrAuthenticationTypeMSN):
|
||||
self = MSN
|
||||
case String(kSecAttrAuthenticationTypeDPA):
|
||||
self = DPA
|
||||
case String(kSecAttrAuthenticationTypeRPA):
|
||||
self = RPA
|
||||
case String(kSecAttrAuthenticationTypeHTTPBasic):
|
||||
self = HTTPBasic
|
||||
case String(kSecAttrAuthenticationTypeHTTPDigest):
|
||||
self = HTTPDigest
|
||||
case String(kSecAttrAuthenticationTypeHTMLForm):
|
||||
self = HTMLForm
|
||||
case String(kSecAttrAuthenticationTypeDefault):
|
||||
self = Default
|
||||
default:
|
||||
self = Default
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .NTLM:
|
||||
return String(kSecAttrAuthenticationTypeNTLM)
|
||||
case .MSN:
|
||||
return String(kSecAttrAuthenticationTypeMSN)
|
||||
case .DPA:
|
||||
return String(kSecAttrAuthenticationTypeDPA)
|
||||
case .RPA:
|
||||
return String(kSecAttrAuthenticationTypeRPA)
|
||||
case .HTTPBasic:
|
||||
return String(kSecAttrAuthenticationTypeHTTPBasic)
|
||||
case .HTTPDigest:
|
||||
return String(kSecAttrAuthenticationTypeHTTPDigest)
|
||||
case .HTMLForm:
|
||||
return String(kSecAttrAuthenticationTypeHTMLForm)
|
||||
case .Default:
|
||||
return String(kSecAttrAuthenticationTypeDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import Foundation
|
||||
|
||||
public enum LocksmithInternetProtocol: RawRepresentable {
|
||||
case FTP, FTPAccount, HTTP, IRC, NNTP, POP3, SMTP, SOCKS, IMAP, LDAP, AppleTalk, AFP, Telnet, SSH, FTPS, HTTPS, HTTPProxy, HTTPSProxy, FTPProxy, SMB, RTSP, RTSPProxy, DAAP, EPPC, IPP, NNTPS, LDAPS, TelnetS, IMAPS, IRCS, POP3S
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case String(kSecAttrProtocolFTP):
|
||||
self = FTP
|
||||
case String(kSecAttrProtocolFTPAccount):
|
||||
self = FTPAccount
|
||||
case String(kSecAttrProtocolHTTP):
|
||||
self = HTTP
|
||||
case String(kSecAttrProtocolIRC):
|
||||
self = IRC
|
||||
case String(kSecAttrProtocolNNTP):
|
||||
self = NNTP
|
||||
case String(kSecAttrProtocolPOP3):
|
||||
self = POP3
|
||||
case String(kSecAttrProtocolSMTP):
|
||||
self = SMTP
|
||||
case String(kSecAttrProtocolSOCKS):
|
||||
self = SOCKS
|
||||
case String(kSecAttrProtocolIMAP):
|
||||
self = IMAP
|
||||
case String(kSecAttrProtocolLDAP):
|
||||
self = LDAP
|
||||
case String(kSecAttrProtocolAppleTalk):
|
||||
self = AppleTalk
|
||||
case String(kSecAttrProtocolAFP):
|
||||
self = AFP
|
||||
case String(kSecAttrProtocolTelnet):
|
||||
self = Telnet
|
||||
case String(kSecAttrProtocolSSH):
|
||||
self = SSH
|
||||
case String(kSecAttrProtocolFTPS):
|
||||
self = FTPS
|
||||
case String(kSecAttrProtocolHTTPS):
|
||||
self = HTTPS
|
||||
case String(kSecAttrProtocolHTTPProxy):
|
||||
self = HTTPProxy
|
||||
case String(kSecAttrProtocolHTTPSProxy):
|
||||
self = HTTPSProxy
|
||||
case String(kSecAttrProtocolFTPProxy):
|
||||
self = FTPProxy
|
||||
case String(kSecAttrProtocolSMB):
|
||||
self = SMB
|
||||
case String(kSecAttrProtocolRTSP):
|
||||
self = RTSP
|
||||
case String(kSecAttrProtocolRTSPProxy):
|
||||
self = RTSPProxy
|
||||
case String(kSecAttrProtocolDAAP):
|
||||
self = DAAP
|
||||
case String(kSecAttrProtocolEPPC):
|
||||
self = EPPC
|
||||
case String(kSecAttrProtocolIPP):
|
||||
self = IPP
|
||||
case String(kSecAttrProtocolNNTPS):
|
||||
self = NNTPS
|
||||
case String(kSecAttrProtocolLDAPS):
|
||||
self = LDAPS
|
||||
case String(kSecAttrProtocolTelnetS):
|
||||
self = TelnetS
|
||||
case String(kSecAttrProtocolIMAPS):
|
||||
self = IMAPS
|
||||
case String(kSecAttrProtocolIRCS):
|
||||
self = IRCS
|
||||
case String(kSecAttrProtocolPOP3S):
|
||||
self = POP3S
|
||||
default:
|
||||
self = HTTP
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .FTP:
|
||||
return String(kSecAttrProtocolFTP)
|
||||
case .FTPAccount:
|
||||
return String(kSecAttrProtocolFTPAccount)
|
||||
case .HTTP:
|
||||
return String(kSecAttrProtocolHTTP)
|
||||
case .IRC:
|
||||
return String(kSecAttrProtocolIRC)
|
||||
case .NNTP:
|
||||
return String(kSecAttrProtocolNNTP)
|
||||
case .POP3:
|
||||
return String(kSecAttrProtocolPOP3)
|
||||
case .SMTP:
|
||||
return String(kSecAttrProtocolSMTP)
|
||||
case .SOCKS:
|
||||
return String(kSecAttrProtocolSOCKS)
|
||||
case .IMAP:
|
||||
return String(kSecAttrProtocolIMAP)
|
||||
case .LDAP:
|
||||
return String(kSecAttrProtocolLDAP)
|
||||
case .AppleTalk:
|
||||
return String(kSecAttrProtocolAppleTalk)
|
||||
case .AFP:
|
||||
return String(kSecAttrProtocolAFP)
|
||||
case .Telnet:
|
||||
return String(kSecAttrProtocolTelnet)
|
||||
case .SSH:
|
||||
return String(kSecAttrProtocolSSH)
|
||||
case .FTPS:
|
||||
return String(kSecAttrProtocolFTPS)
|
||||
case .HTTPS:
|
||||
return String(kSecAttrProtocolHTTPS)
|
||||
case .HTTPProxy:
|
||||
return String(kSecAttrProtocolHTTPProxy)
|
||||
case .HTTPSProxy:
|
||||
return String(kSecAttrProtocolHTTPSProxy)
|
||||
case .FTPProxy:
|
||||
return String(kSecAttrProtocolFTPProxy)
|
||||
case .SMB:
|
||||
return String(kSecAttrProtocolSMB)
|
||||
case .RTSP:
|
||||
return String(kSecAttrProtocolRTSP)
|
||||
case .RTSPProxy:
|
||||
return String(kSecAttrProtocolRTSPProxy)
|
||||
case .DAAP:
|
||||
return String(kSecAttrProtocolDAAP)
|
||||
case .EPPC:
|
||||
return String(kSecAttrProtocolEPPC)
|
||||
case .IPP:
|
||||
return String(kSecAttrProtocolIPP)
|
||||
case .NNTPS:
|
||||
return String(kSecAttrProtocolNNTPS)
|
||||
case .LDAPS:
|
||||
return String(kSecAttrProtocolLDAPS)
|
||||
case .TelnetS:
|
||||
return String(kSecAttrProtocolTelnetS)
|
||||
case .IMAPS:
|
||||
return String(kSecAttrProtocolIMAPS)
|
||||
case .IRCS:
|
||||
return String(kSecAttrProtocolIRCS)
|
||||
case .POP3S:
|
||||
return String(kSecAttrProtocolPOP3S)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import Foundation
|
||||
|
||||
// With thanks to http://iosdeveloperzone.com/2014/10/22/taming-foundation-constants-into-swift-enums/
|
||||
// MARK: Security Class
|
||||
public enum LocksmithSecurityClass: RawRepresentable {
|
||||
case GenericPassword, InternetPassword, Certificate, Key, Identity
|
||||
|
||||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case String(kSecClassGenericPassword):
|
||||
self = GenericPassword
|
||||
case String(kSecClassInternetPassword):
|
||||
self = InternetPassword
|
||||
case String(kSecClassCertificate):
|
||||
self = Certificate
|
||||
case String(kSecClassKey):
|
||||
self = Key
|
||||
case String(kSecClassIdentity):
|
||||
self = Identity
|
||||
default:
|
||||
print("SecurityClass: Invalid raw value provided. Defaulting to .GenericPassword")
|
||||
self = GenericPassword
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: String {
|
||||
switch self {
|
||||
case .GenericPassword:
|
||||
return String(kSecClassGenericPassword)
|
||||
case .InternetPassword:
|
||||
return String(kSecClassInternetPassword)
|
||||
case .Certificate:
|
||||
return String(kSecClassCertificate)
|
||||
case .Key:
|
||||
return String(kSecClassKey)
|
||||
case .Identity:
|
||||
return String(kSecClassIdentity)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
//
|
||||
// LocksmithTests.swift
|
||||
// LocksmithTests
|
||||
//
|
||||
// Created by Matthew Palmer on 27/06/2015.
|
||||
// Copyright © 2015 Matthew Palmer. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Locksmith
|
||||
|
||||
class LocksmithTests: XCTestCase {
|
||||
let userAccount = "myUser"
|
||||
let service = "myService"
|
||||
|
||||
typealias TestingDictionaryType = [String: String]
|
||||
|
||||
func clear() {
|
||||
do {
|
||||
try Locksmith.deleteDataForUserAccount(userAccount, inService: service)
|
||||
try Locksmith.deleteDataForUserAccount(userAccount)
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
override func setUp() {
|
||||
clear()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
clear()
|
||||
}
|
||||
|
||||
func testStaticMethods() {
|
||||
let data = ["some": "data"]
|
||||
try! Locksmith.saveData(data, forUserAccount: userAccount, inService: service)
|
||||
|
||||
let loaded = Locksmith.loadDataForUserAccount(userAccount, inService: service)! as! TestingDictionaryType
|
||||
XCTAssertEqual(loaded, data)
|
||||
|
||||
try! Locksmith.deleteDataForUserAccount(userAccount, inService: service)
|
||||
|
||||
let otherData: TestingDictionaryType = ["something": "way different"]
|
||||
try! Locksmith.saveData(otherData, forUserAccount: userAccount, inService: service)
|
||||
|
||||
let loadedAgain = Locksmith.loadDataForUserAccount(userAccount, inService: service)! as! TestingDictionaryType
|
||||
XCTAssertEqual(loadedAgain, otherData)
|
||||
|
||||
let updatedData = ["this update": "brings the ruckus"]
|
||||
try! Locksmith.updateData(updatedData, forUserAccount: userAccount, inService: service)
|
||||
|
||||
let loaded3 = Locksmith.loadDataForUserAccount(userAccount, inService: service)! as! TestingDictionaryType
|
||||
|
||||
XCTAssertEqual(loaded3, updatedData)
|
||||
}
|
||||
|
||||
func testStaticMethodsForDefaultService() {
|
||||
let data = ["some": "data"]
|
||||
try! Locksmith.saveData(data, forUserAccount: userAccount)
|
||||
|
||||
let loaded = Locksmith.loadDataForUserAccount(userAccount)! as! TestingDictionaryType
|
||||
XCTAssertEqual(loaded, data)
|
||||
|
||||
try! Locksmith.deleteDataForUserAccount(userAccount)
|
||||
|
||||
let otherData: TestingDictionaryType = ["something": "way different"]
|
||||
try! Locksmith.saveData(otherData, forUserAccount: userAccount)
|
||||
|
||||
let loadedAgain = Locksmith.loadDataForUserAccount(userAccount)! as! TestingDictionaryType
|
||||
XCTAssertEqual(loadedAgain, otherData)
|
||||
|
||||
let updatedData = ["this update": "brings the ruckus"]
|
||||
try! Locksmith.updateData(updatedData, forUserAccount: userAccount)
|
||||
|
||||
let loaded3 = Locksmith.loadDataForUserAccount(userAccount)! as! TestingDictionaryType
|
||||
|
||||
XCTAssertEqual(loaded3, updatedData)
|
||||
}
|
||||
|
||||
func createGenericPasswordWithData(data: [String: AnyObject]) {
|
||||
struct CreateGenericPassword: CreateableSecureStorable, GenericPasswordSecureStorable {
|
||||
let data: [String: AnyObject]
|
||||
let account: String
|
||||
let service: String
|
||||
}
|
||||
|
||||
let create = CreateGenericPassword(data: data, account: userAccount, service: service)
|
||||
try! create.createInSecureStore() // make sure it doesn't throw
|
||||
}
|
||||
|
||||
func testCreateForGenericPassword() {
|
||||
let data = ["some": "data"]
|
||||
createGenericPasswordWithData(data)
|
||||
}
|
||||
|
||||
func testLoadForGenericPassword() {
|
||||
let data = ["one": "two"]
|
||||
createGenericPasswordWithData(data)
|
||||
|
||||
struct ReadGenericPassword: ReadableSecureStorable, GenericPasswordSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
}
|
||||
|
||||
let read = ReadGenericPassword(account: userAccount, service: service)
|
||||
let actual = read.readFromSecureStore()!.data as! TestingDictionaryType
|
||||
XCTAssertEqual(actual, data)
|
||||
}
|
||||
|
||||
func testDeleteForGenericPassword() {
|
||||
let initialData = ["one": "two"]
|
||||
|
||||
createGenericPasswordWithData(initialData)
|
||||
|
||||
struct DeleteGenericPassword: DeleteableSecureStorable, GenericPasswordSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
}
|
||||
|
||||
let delete = DeleteGenericPassword(account: userAccount, service: service)
|
||||
try! delete.deleteFromSecureStore()
|
||||
|
||||
let d = Locksmith.loadDataForUserAccount(userAccount, inService: service)
|
||||
XCTAssertNil(d)
|
||||
}
|
||||
|
||||
func testForConformanceToAll3Protocols() {
|
||||
struct Omnivore: ReadableSecureStorable, CreateableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
let data: [String: AnyObject]
|
||||
}
|
||||
|
||||
let data: [String: String] = ["something": "else"]
|
||||
let omni = Omnivore(account: userAccount, service: service, data: data)
|
||||
|
||||
try! omni.createInSecureStore()
|
||||
|
||||
let result = omni.readFromSecureStore()
|
||||
let resultData = result?.data as! [String: String]
|
||||
|
||||
XCTAssertEqual(result?.account, userAccount)
|
||||
XCTAssertEqual(result?.service, service)
|
||||
XCTAssertEqual(resultData, data)
|
||||
|
||||
try! omni.deleteFromSecureStore()
|
||||
|
||||
let noResult = omni.readFromSecureStore()
|
||||
XCTAssertNil(noResult?.service)
|
||||
|
||||
try! omni.createInSecureStore()
|
||||
XCTAssertEqual(result?.account, userAccount)
|
||||
XCTAssertEqual(result?.service, service)
|
||||
XCTAssertEqual(resultData, data)
|
||||
}
|
||||
|
||||
func testDeleteForInternetPassword() {
|
||||
struct Create : CreateableSecureStorable, InternetPasswordSecureStorable {
|
||||
let account: String
|
||||
let server: String
|
||||
let data: [String: AnyObject]
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
}
|
||||
|
||||
let server = "server"
|
||||
let initialData = ["one": "two"]
|
||||
let port = 8080
|
||||
let internetProtocol = LocksmithInternetProtocol.HTTPS
|
||||
let authenticationType = LocksmithInternetAuthenticationType.DPA
|
||||
|
||||
struct Delete: DeleteableSecureStorable, InternetPasswordSecureStorable {
|
||||
let account: String
|
||||
let server: String
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
}
|
||||
|
||||
struct Read: ReadableSecureStorable, InternetPasswordSecureStorable {
|
||||
let account: String
|
||||
let server: String
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
}
|
||||
|
||||
let c = Create(account: userAccount, server: server, data: initialData, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
|
||||
try! c.createInSecureStore()
|
||||
let r1 = Read(account: userAccount, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
|
||||
let result1 = r1.readFromSecureStore()
|
||||
XCTAssertEqual(result1?.server, server)
|
||||
|
||||
let d = Delete(account: userAccount, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
|
||||
try! d.deleteFromSecureStore()
|
||||
|
||||
let result2 = r1.readFromSecureStore()
|
||||
XCTAssertNil(result2?.server)
|
||||
}
|
||||
|
||||
func testGenericPasswordMetaAttributesAreCreatedAndReturned() {
|
||||
struct Create: CreateableSecureStorable, GenericPasswordSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
let comment: String?
|
||||
let description: String?
|
||||
let creator: UInt?
|
||||
let data: [String: AnyObject]
|
||||
}
|
||||
|
||||
let initialData = ["one": "two"]
|
||||
let creator: UInt = 5
|
||||
let comment = "this is a comment"
|
||||
let description = "this is the description"
|
||||
let c = Create(account: userAccount, service: service, comment: comment, description: description, creator: creator, data: initialData)
|
||||
try! c.createInSecureStore()
|
||||
|
||||
struct Read: ReadableSecureStorable, GenericPasswordSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
}
|
||||
|
||||
let r = Read(account: userAccount, service: service)
|
||||
let d = r.readFromSecureStore()
|
||||
|
||||
XCTAssertEqual(d?.account, userAccount)
|
||||
XCTAssertEqual(d?.service, service)
|
||||
XCTAssertEqual(d!.data as! [String: String], initialData)
|
||||
XCTAssertEqual(d?.creator, creator)
|
||||
XCTAssertEqual(d?.comment, comment)
|
||||
XCTAssertEqual(d?.description, description)
|
||||
|
||||
XCTAssertNil(d?.generic)
|
||||
XCTAssertNil(d?.isInvisible)
|
||||
}
|
||||
|
||||
func testInternetPasswordMetaAttributesAreCreatedAndReturned() {
|
||||
struct CreateInternetPassword: CreateableSecureStorable, InternetPasswordSecureStorable {
|
||||
let account: String
|
||||
let data: [String: AnyObject]
|
||||
let server: String
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
let path: String?
|
||||
let securityDomain: String?
|
||||
}
|
||||
|
||||
let userAccount = "user \(NSDate())"
|
||||
let initialData = ["internet": "data"]
|
||||
let server = "net.matthewpalmer"
|
||||
let port = 8080
|
||||
let internetProtocol = LocksmithInternetProtocol.FTP
|
||||
let authenticationType = LocksmithInternetAuthenticationType.HTTPBasic
|
||||
let path = "somePath"
|
||||
let securityDomain = "someDomain"
|
||||
|
||||
struct ReadInternetPassword: ReadableSecureStorable, InternetPasswordSecureStorable {
|
||||
let account: String
|
||||
let server: String
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
}
|
||||
|
||||
let c = CreateInternetPassword(account: userAccount, data: initialData, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType, path: path, securityDomain: securityDomain)
|
||||
try! c.createInSecureStore()
|
||||
|
||||
let r = ReadInternetPassword(account: userAccount, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
|
||||
let result = r.readFromSecureStore()
|
||||
|
||||
XCTAssertEqual(result?.account, userAccount)
|
||||
XCTAssertEqual(result!.data as! [String: String], initialData)
|
||||
XCTAssertEqual(result?.server, server)
|
||||
XCTAssertEqual(result?.port, port)
|
||||
XCTAssertEqual(result?.internetProtocol, internetProtocol)
|
||||
XCTAssertEqual(result?.authenticationType, authenticationType)
|
||||
XCTAssertEqual(result?.securityDomain, securityDomain)
|
||||
XCTAssertEqual(result?.path, path)
|
||||
}
|
||||
|
||||
func assertStringPairsMatchInDictionary(dictionary: NSDictionary, pairs: [(key: CFString, expectedOutput: String)]) {
|
||||
for pair in pairs {
|
||||
let a = dictionary[String(pair.0)] as! CFStringRef
|
||||
XCTAssertEqual(a as String, pair.1)
|
||||
}
|
||||
}
|
||||
|
||||
func testInternetPasswordAttributesAreAppliedForConformingTypes() {
|
||||
struct CreateInternetPassword: CreateableSecureStorable, InternetPasswordSecureStorable, DeleteableSecureStorable {
|
||||
let account: String
|
||||
let service: String
|
||||
let data: [String: AnyObject]
|
||||
let server: String
|
||||
let port: Int
|
||||
let internetProtocol: LocksmithInternetProtocol
|
||||
let authenticationType: LocksmithInternetAuthenticationType
|
||||
let path: String?
|
||||
let securityDomain: String?
|
||||
let performCreateRequestClosure: PerformRequestClosureType
|
||||
}
|
||||
|
||||
let account = "myUser"
|
||||
let port = 8080
|
||||
let internetProtocol = LocksmithInternetProtocol.HTTP
|
||||
let authenticationType = LocksmithInternetAuthenticationType.HTTPBasic
|
||||
let path = "some_path"
|
||||
let securityDomain = "secdomain"
|
||||
let data = ["some": "data"]
|
||||
let server = "server"
|
||||
|
||||
let expect = expectationWithDescription("Must enter the closure")
|
||||
|
||||
let performRequestClosure: PerformRequestClosureType = { (requestReference, result) in
|
||||
let dict = requestReference as NSDictionary
|
||||
|
||||
self.assertStringPairsMatchInDictionary(dict, pairs: [
|
||||
(kSecAttrAccount, account),
|
||||
(kSecAttrProtocol, internetProtocol.rawValue),
|
||||
(kSecAttrAuthenticationType, authenticationType.rawValue),
|
||||
(kSecAttrPath, path),
|
||||
(kSecAttrSecurityDomain, securityDomain),
|
||||
(kSecAttrServer, server),
|
||||
(kSecClass, String(kSecClassInternetPassword))
|
||||
])
|
||||
|
||||
let p = dict[String(kSecAttrPort)] as! CFNumberRef
|
||||
XCTAssertEqual(p as Int, port)
|
||||
|
||||
expect.fulfill()
|
||||
|
||||
return errSecSuccess
|
||||
}
|
||||
|
||||
let create = CreateInternetPassword(account: account, service: service, data: data, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType, path: path, securityDomain: securityDomain, performCreateRequestClosure: performRequestClosure)
|
||||
do { try create.deleteFromSecureStore() } catch {}
|
||||
try! create.createInSecureStore()
|
||||
|
||||
waitForExpectationsWithTimeout(0.1, handler: nil)
|
||||
}
|
||||
|
||||
func testGenericPasswordOptionalAttributesAreAppliedForConformingTypes() {
|
||||
struct CreateGenericPassword: CreateableSecureStorable, GenericPasswordSecureStorable {
|
||||
let data: [String: AnyObject]
|
||||
let account: String
|
||||
let service: String
|
||||
let accessGroup: String?
|
||||
let description: String?
|
||||
let creator: UInt?
|
||||
var performCreateRequestClosure: PerformRequestClosureType
|
||||
let accessible: LocksmithAccessibleOption?
|
||||
let comment: String?
|
||||
let type: UInt?
|
||||
let isInvisible: Bool?
|
||||
let isNegative: Bool?
|
||||
let generic: NSData?
|
||||
}
|
||||
|
||||
let data: [String: AnyObject] = ["some": "data"]
|
||||
let account: String = "myUser"
|
||||
let service: String = "myService"
|
||||
let accessGroup: String = "myAccessGroup"
|
||||
let description: String = "myDescription"
|
||||
let creator: UInt = 5
|
||||
let accessible: LocksmithAccessibleOption = LocksmithAccessibleOption.Always
|
||||
let comment: String = "myComment"
|
||||
let type: UInt = 10
|
||||
let isInvisible: Bool = false
|
||||
let isNegative: Bool = false
|
||||
let generic: NSData = NSData()
|
||||
|
||||
let expect = expectationWithDescription("Must enter the closure")
|
||||
|
||||
let performRequestClosure: PerformRequestClosureType = { (requestReference, result) in
|
||||
let dict = requestReference as NSDictionary
|
||||
|
||||
self.assertStringPairsMatchInDictionary(dict, pairs: [
|
||||
(kSecAttrAccount, account),
|
||||
(kSecAttrService, service),
|
||||
(kSecAttrAccessGroup, accessGroup),
|
||||
(kSecAttrDescription, description),
|
||||
(kSecAttrComment, comment),
|
||||
(kSecAttrAccessible, accessible.rawValue),
|
||||
(kSecClass, String(kSecClassGenericPassword))
|
||||
])
|
||||
|
||||
let cr = dict[String(kSecAttrCreator)] as! CFNumberRef
|
||||
XCTAssertEqual(cr as UInt, creator)
|
||||
|
||||
let ty = dict[String(kSecAttrType)] as! CFNumberRef
|
||||
XCTAssertEqual(ty as UInt, type)
|
||||
|
||||
let inv = dict[String(kSecAttrIsInvisible)] as! CFBooleanRef
|
||||
XCTAssertEqual(inv as Bool, isInvisible)
|
||||
|
||||
let neg = dict[String(kSecAttrIsNegative)] as! CFBooleanRef
|
||||
XCTAssertEqual(neg as Bool, isNegative)
|
||||
|
||||
let gen = dict[String(kSecAttrGeneric)] as! CFDataRef
|
||||
XCTAssertEqual(gen, generic)
|
||||
|
||||
expect.fulfill()
|
||||
|
||||
return errSecSuccess
|
||||
}
|
||||
|
||||
let create: CreateGenericPassword = CreateGenericPassword(data: data, account: account, service: service, accessGroup: accessGroup, description: description, creator: creator, performCreateRequestClosure: performRequestClosure, accessible: accessible, comment: comment, type: type, isInvisible: isInvisible, isNegative: isNegative, generic: generic)
|
||||
|
||||
try! create.createInSecureStore()
|
||||
|
||||
waitForExpectationsWithTimeout(0.1, handler: nil)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user