Compare commits

...

206 Commits

Author SHA1 Message Date
Matthew Palmer 5e9fc402dd Update README.md 2016-08-19 13:27:14 +10:00
Matthew Palmer 4010a3303e Update README.md 2016-08-19 13:26:59 +10:00
Matthew Palmer 955f12033c Update README.md 2016-08-19 13:25:37 +10:00
Matthew Palmer 798405f93a Update README.md 2016-08-19 13:25:14 +10:00
Matthew Palmer 299bb3fa3f Update README.md 2016-08-19 13:22:52 +10:00
Matthew Palmer 6814e7f7aa Update README.md 2016-08-19 13:22:30 +10:00
Matthew Palmer 173ab46b6e Update README.md 2016-08-19 07:46:20 +10:00
matthewpalmer f2269ef8d1 Update rocket link 2016-08-13 11:58:01 +10:00
matthewpalmer 4d00c74922 Add padding symbols 2016-08-13 11:56:33 +10:00
matthewpalmer 1e8fcc8c2c Add spacing over heading for rocket section 2016-08-13 11:53:55 +10:00
matthewpalmer 9e1e912788 Update rocket link position and style 2016-08-13 11:53:13 +10:00
matthewpalmer c6838a31f2 Add link to Rocket to README 2016-08-13 11:44:55 +10:00
Matthew Palmer cdd1b2893a Merge pull request #127 from Podfactor/app-extension-safe
Fix warning when linking against Locksmith from an app extension
2016-07-09 06:38:03 +10:00
Tom Brow 7b9237c39e build with APPLICATION_EXTENSION_API_ONLY = YES 2016-05-31 17:25:45 -05:00
Matthew Palmer 1b278e59a5 Merge pull request #118 from jakemarsh/master
Carthage doesn't allow single quotes. Change README to doubles.
2016-04-09 13:25:19 +10:00
Jake Marsh 1359edf840 Update README.md
Carthage doesn't allow single quotes. Change to doubles.
2016-04-06 19:24:58 -07:00
matthewpalmer 6b32c7ec68 Fix regression in update method to fix #110 2016-02-16 20:23:02 +11:00
Matthew Palmer 80df919791 Merge pull request #108 from matthewpalmer/2.0.7
2.0.7
2016-02-14 18:24:59 +11:00
matthewpalmer 2f54712fda Bump podspec 2016-02-14 18:17:57 +11:00
matthewpalmer 78060ecc28 Add extra tests for new update method 2016-02-14 18:15:06 +11:00
Sergey Galezdinov e04d6291d4 Use SecItemUpdate function for updating the data. Added missing updateInSecureStore method to CreateableSecureStorable 2016-02-11 02:46:34 +03:00
Matthew Palmer c29ebcdba8 Merge pull request #102 from gilt/bitcode
full bitcode is needed for running Debug on an Apple TV device, even in Debug mode
2016-01-24 10:39:30 +11:00
Matthew Palmer ed56b9015c Merge pull request #101 from gilt/build_all_architectures
don't set ONLY_ACTIVE_ARCH to YES
2016-01-24 10:39:06 +11:00
Evan Maloney b5f7410d0e full bitcode is needed for running Debug on device
without this setting, it won't be possible to run a Debug build on the device; 'marker' only works in the sim
2016-01-22 14:24:00 -05:00
Evan Maloney e002690ccf don't set ONLY_ACTIVE_ARCH to YES
When embedding this project directly into another Xcode project in a target that:

• Is being built for a platform that has a simulator/device dichotomy (iOS, tvOS, watchOS), and
• Is inside a parent project building a Debug configuration

You will get one of two kinds of errors:

• Either the framework module itself is missing at the 'import' level, or
• Some or all of the framework's public symbols can't be found

The solution is to disable ONLY_ACTIVE_ARCH, which will ensure that both processor architectures are built and the symbols can be found.
2016-01-22 12:23:25 -05:00
Matthew Palmer 6ae6aaedf2 Merge pull request #93 from gambcl/gambcl-carthage-tvos
Added support for tvOS for Carthage builds
2015-12-15 07:31:43 +11:00
Charles Gamble c75cccde53 Change tvOS Deployment Target to 9.0 2015-12-14 13:12:55 +00:00
Matthew Palmer ecef17994c Update README.md 2015-12-11 20:51:19 +11:00
Matthew Palmer 55d9ad27a3 Merge pull request #87 from tc/master
carthage install instructions
2015-12-11 20:50:26 +11:00
Matthew Palmer 3393682ee7 Merge pull request #91 from juliangrosshauser/remove-info-plist-copy-bundle-resources
Remove Info.plist from Copy Bundle Resources phase
2015-12-11 20:49:53 +11:00
Matthew Palmer ff6e09e051 Merge pull request #90 from emrekyv/remove-print-statements
Remove print statements
2015-12-11 20:49:26 +11:00
Julian Grosshauser 6298dc0ec6 Remove Info.plist from Copy Bundle Resources phase
The removal of Info.plist from the Copy Bundle Resources build phase of the iOS target fixes the following warning:

Warning: The Copy Bundle Resources build phase contains this target's Info.plist file 'Source/Info.plist'.
2015-12-11 00:55:13 +01:00
Emre Kucukayvaz 96d1958632 Remove print statements 2015-12-10 10:47:03 +01:00
Charles Gamble ee5351dfe9 Added carthage support for tvOS build 2015-12-09 13:55:10 +00:00
Matthew Palmer 2459d0c2ff Update README.md 2015-11-23 08:19:06 +11:00
Matthew Palmer effbd18736 Merge pull request #83 from matthewpalmer/matthewpalmer-tvos
Re-add tvOS support
2015-11-22 20:20:41 +11:00
Tommy Chheng bb380ebd3b carthage install instructions 2015-11-16 14:24:33 -08:00
Matthew Palmer 4d9fcd7f77 Update README.md 2015-10-15 06:46:08 +11:00
Matthew Palmer 2ed51c13e7 Update README.md 2015-10-15 06:45:57 +11:00
Matthew Palmer 60a44fe471 Re-add tvOS support 2015-10-15 06:45:02 +11:00
Matthew Palmer da2ea377bc Merge pull request #82 from matthewpalmer/revert-81-master
Revert "Add support for tvOS"
2015-10-15 06:42:32 +11:00
Matthew Palmer 90fb6207ae Revert "Add support for tvOS" 2015-10-15 06:41:29 +11:00
Matthew Palmer e91be340b8 Update README.md 2015-10-15 06:32:28 +11:00
Matthew Palmer db0802cdaf Merge pull request #81 from victor/master
Add support for tvOS
2015-10-15 06:32:06 +11:00
Victor Jalencas 03ff3ae76a Add support for tvOS 2015-10-14 17:19:38 +02:00
Matthew Palmer f086127b46 Add links to Apple's docs
Fixes #78
2015-10-04 09:30:17 +11:00
matthewpalmer b36864e1da Bump version number. Fixes #77 2015-10-04 09:08:31 +11:00
matthewpalmer ecd63efd8b Bump to 2.0.2 2015-09-27 19:32:52 +10:00
matthewpalmer f3afe2d968 Merge branch 'jankase-master' into version-2.0.2
* jankase-master:
  fixed crash when store is empty
2015-09-27 19:31:21 +10:00
Jan Kase b096465a33 fixed crash when store is empty 2015-09-26 16:17:19 +02:00
Jan Kase 157e4531f1 Merge commit '3b2523e89e6c84c05386517183fa7b6c43ddbf50' 2015-09-26 16:09:57 +02:00
Matthew Palmer 3b2523e89e Merge pull request #67 from hermanolsson/master
Add umbrella header to all three targets.
2015-09-22 07:33:57 +10:00
Herman Olsson a8a1abca7b Add umbrella header to all three targets. 2015-09-21 17:10:07 +02:00
matthewpalmer eb4d2f8351 Merge branch 'tucali-LocksmithExample'
* tucali-LocksmithExample:
  Simplify example project
  Added basic iOS and wOS example.
2015-09-13 08:43:12 +10:00
matthewpalmer f7b5ee5c57 Simplify example project 2015-09-13 08:42:55 +10:00
Tai Heng c6af051213 Added basic iOS and wOS example. 2015-09-12 11:12:30 +01:00
matthewpalmer 2eec09637f Update travis 2015-09-12 16:19:14 +10:00
matthewpalmer 477977a11c Update deployment target for example 2015-09-12 16:13:33 +10:00
matthewpalmer 1830acd0ee Update travis 2015-09-12 16:06:45 +10:00
matthewpalmer 0f06f2d911 Update README 2015-09-12 16:06:36 +10:00
matthewpalmer 3910c07e5b CI for everything 2015-09-12 15:57:03 +10:00
matthewpalmer e79e4aeb4e Update gitignore 2015-09-12 15:52:44 +10:00
matthewpalmer f2b44e6a1c Update travis 2015-09-12 15:48:53 +10:00
matthewpalmer 100fe4c13c Add iOS example app 2015-09-12 15:45:34 +10:00
matthewpalmer 562a68f2bd Tweak travis 2015-09-12 15:20:25 +10:00
matthewpalmer 373edfae08 Add watchOS scheme and tweak travis 2015-09-12 15:13:56 +10:00
matthewpalmer 31d6fc2ef7 Update travis 2015-09-12 09:56:47 +10:00
matthewpalmer 76cfd3c3e4 Update travis build file 2015-09-12 09:52:23 +10:00
matthewpalmer 2ebf806a1a Update README and make schemes shared 2015-09-12 09:49:49 +10:00
matthewpalmer 22bcd61cff Add watchOS target
* Note that this is untested and I don't know if it actually
  works at all
2015-09-12 09:40:02 +10:00
matthewpalmer f8e007ebda Update Travis 2015-09-12 09:23:02 +10:00
matthewpalmer 267fe346c9 Remove 1.2.2 note from README 2015-09-12 09:13:53 +10:00
matthewpalmer b4ebf6f318 Update metadata 2015-09-12 09:11:38 +10:00
matthewpalmer 8496e635c1 Clean up project structure 2015-09-12 08:50:33 +10:00
Jan Kaše cbcada16dc Merge pull request #2 from matthewpalmer/master
update
2015-09-08 16:40:14 +02:00
matthewpalmer 17579e4417 Add iOS compatibility checks 2015-09-08 22:56:21 +10:00
matthewpalmer c0906fbda3 Merge remote-tracking branch 'garnett/nil-asserts' into garnett-remove-uikit
* garnett/nil-asserts:
  Use nill asserts where possible
2015-09-08 22:25:28 +10:00
matthewpalmer b61c058597 Merge remote-tracking branch 'garnett/swifty-style' into garnett-remove-uikit
* garnett/swifty-style:
  Remove explict self where possible
2015-09-08 22:24:42 +10:00
Denis Lebedev 323cae20ec Use nill asserts where possible 2015-09-06 22:04:33 +01:00
Denis Lebedev e99470b94a Remove explict self where possible 2015-09-06 21:55:54 +01:00
Denis Lebedev 1aae774695 Remove UIKit dependency 2015-09-06 21:46:45 +01:00
Matthew Palmer 7bf3ad867f Update README.md 2015-09-05 09:09:01 +10:00
matthewpalmer 002ce89a89 Merge branch 'nvh-master'
* nvh-master:
  Added new files to project
  Removed old tests
2015-09-04 20:49:15 +10:00
matthewpalmer ef4a62f475 Fix iOS requirement and update travis 2015-09-04 20:48:38 +10:00
Niels van Hoorn d09f7af9d7 Added new files to project 2015-09-02 12:01:39 +02:00
Niels van Hoorn 9e99695143 Removed old tests 2015-09-02 12:01:30 +02:00
Matthew Palmer 27fbf4e935 Update README.md 2015-08-31 22:04:54 +10:00
matthewpalmer 7a6f2a6724 Add link to blog post 2015-08-31 21:58:50 +10:00
matthewpalmer 1bca5f35fb Fix typo in README 2015-08-31 21:43:17 +10:00
Matthew Palmer 1e3b72b0b6 Merge pull request #52 from matthewpalmer/swift-2.0-protocol
Adopt a protocol-oriented design
2015-08-31 21:39:57 +10:00
matthewpalmer 41b6644b18 Set version to 2.0.0 2015-08-31 21:38:49 +10:00
matthewpalmer a449ebf527 Update compatibility branch name 2015-08-31 21:29:59 +10:00
matthewpalmer ccda8fb901 Update README 2015-08-31 21:02:40 +10:00
matthewpalmer f94eff2115 Update README 2015-08-31 20:47:32 +10:00
matthewpalmer ee96b18cfd Add tests to clarify closure bug 2015-08-31 19:42:32 +10:00
matthewpalmer 79cf22216f Refactor into separate files and fix closure bug 2015-08-31 19:42:16 +10:00
matthewpalmer 4bfbf86f73 Add the ability to conform to all 3 protocols 2015-08-31 18:58:01 +10:00
matthewpalmer f0c4572ff4 Add test for deleting internet password 2015-08-31 18:27:11 +10:00
matthewpalmer af441ab648 Add internet password request metadata support 2015-08-30 22:23:24 +10:00
matthewpalmer 6074c2640a Add ability to retrieve result metadata from request 2015-08-30 20:31:58 +10:00
matthewpalmer 351b0a4c82 Update readme with code fix 2015-08-30 15:23:09 +10:00
matthewpalmer 38b29df3ca Update installation steps 2015-08-30 14:22:15 +10:00
matthewpalmer 9efa16d266 Remove br 2015-08-30 14:15:26 +10:00
matthewpalmer 709d323463 Fix README typos 2015-08-30 14:14:46 +10:00
matthewpalmer e06271dd90 Major rewrite to be protocol-oriented 2015-08-30 14:06:48 +10:00
matthewpalmer 1b40db9405 Add loading all data for a service.
* Note: this only works for .GenericPasswords. I think I've found
  a bug in trying to load other types of security classes,
  so will need to investigate that further
2015-08-23 19:39:00 +10:00
matthewpalmer afe4706789 Manually merge change to [String: AnyObject] 2015-08-22 22:21:37 +10:00
matthewpalmer a89f2584ce Merge branch 'kayvink-master' into swift-2.0-further-changes
* kayvink-master:
2015-08-22 22:14:22 +10:00
matthewpalmer cb920c0318 Merge branch 'master' of https://github.com/kayvink/Locksmith into kayvink-master
* 'master' of https://github.com/kayvink/Locksmith:
  - Changed saveData from Dictionary<String,String> to Dictionary<String,AnyObjec> - Changed updateData from Dictionary<String,String> to Dictionary<String,AnyObjec>
  - Changed NSDictionary to Dictionary<String:AnyObject> - Added support for other file types beside String
  Update README.md
2015-08-22 22:10:45 +10:00
matthewpalmer b11dea36b4 Merge branch '2.0' into getaaron-keychain-enumeration
* 2.0:
  Update version number
  Update README to reflect Swift 2 changes
  Change loadData method to be non-throwing
  Update tests for new error handling mechanisms
  Refactor for Swift 2
  Major update for Swift 2
  Add tests for Swift 2 style errors
  Update README.md
  + Updates to the new Swift 2 syntax
2015-08-22 22:04:05 +10:00
matthewpalmer 07a9234d3e Merge branch 'master' of https://github.com/kayvink/Locksmith into kayvink-master
* 'master' of https://github.com/kayvink/Locksmith:
  - Changed saveData from Dictionary<String,String> to Dictionary<String,AnyObjec> - Changed updateData from Dictionary<String,String> to Dictionary<String,AnyObjec>
  - Changed NSDictionary to Dictionary<String:AnyObject> - Added support for other file types beside String
2015-08-22 21:22:16 +10:00
matthewpalmer b193e300a5 Add fixes from #38 2015-08-22 21:05:06 +10:00
matthewpalmer 7e53fc8322 Merge branch 'keychain-enumeration' of https://github.com/getaaron/Locksmith into getaaron-keychain-enumeration
* 'keychain-enumeration' of https://github.com/getaaron/Locksmith:
  Make user account optional
  Deprecate .Many and replace it with .All
  Initial implementation of each
  Update README.md
2015-08-22 20:48:13 +10:00
Kay Vink 9ce641f262 - Changed saveData from Dictionary<String,String> to Dictionary<String,AnyObjec>
- Changed updateData from Dictionary<String,String> to Dictionary<String,AnyObjec>

- Changed LocksmithRequest data from NSDictionary? to Dictionary<String,Anyobject>?
- Updated init accordingly
2015-08-21 16:13:45 +02:00
Kay Vink 17d52574ab - Changed NSDictionary to Dictionary<String:AnyObject>
- Added support for other file types beside String
2015-08-21 15:53:57 +02:00
Aaron Brager 5602015a6d Make user account optional 2015-08-12 12:39:29 +02:00
Aaron Brager 737c262e8b Deprecate .Many and replace it with .All 2015-08-12 12:02:40 +02:00
Aaron Brager 8dc3ef57c6 Initial implementation of each 2015-08-12 12:02:03 +02:00
Matthew Palmer 48f008a26e Update README.md 2015-08-11 22:30:00 +10:00
matthewpalmer 7a6395f189 Update version number 2015-06-27 15:42:53 +10:00
matthewpalmer c48ce652bd Update README to reflect Swift 2 changes 2015-06-27 15:42:05 +10:00
matthewpalmer 716759b767 Change loadData method to be non-throwing 2015-06-27 15:36:29 +10:00
matthewpalmer 3da0e97cc6 Update tests for new error handling mechanisms 2015-06-27 10:50:20 +10:00
matthewpalmer 03b5015f4f Refactor for Swift 2
* Make more extensive use of enums for Accessible and SecurityClass
  This wraps up the kSecXXX values much more nicely, and makes them
  more easily accessible through the provided enums
2015-06-27 10:46:24 +10:00
matthewpalmer 1b0c092708 Major update for Swift 2
* Add Swift 2-style error handling: many functions now 'throw'
* Add error types conforming to ErrorType, which are thrown
* Consolidate error processing and information sources to be
  in a single enum
* Refactor and clean up logic as a result of these improvements
  in error handling
2015-06-27 10:44:27 +10:00
matthewpalmer 25917eaf15 Add tests for Swift 2 style errors
* Many functions will now throw instead of returning an NSError
* I have run these in a separate demo project, but not as part of
  the main project (Cocoapods/testing woes continue). If you want
  to run these tests standalone, I've put them in a Gist here
  https://gist.github.com/matthewpalmer/17f54354a710828ec193
* Most of these tests use try!, though they could probably be
  improved to use do/try/catch
2015-06-27 10:42:03 +10:00
Matthew Palmer 0dda3a7208 Update README.md
Add note on Swift 2 support
2015-06-23 07:42:03 +10:00
Matthew Palmer 4dce464fb0 Update README.md
Add README note for installing the 2.0 branch
2015-06-23 07:38:07 +10:00
matthewpalmer 5e508a7551 Merge branch 'SirWellington-feature/swift-2.0-update' into 2.0
* SirWellington-feature/swift-2.0-update:
  + Updates to the new Swift 2 syntax
2015-06-23 07:32:39 +10:00
matthewpalmer a2e58ce87f Merge branch 'feature/swift-2.0-update' of https://github.com/SirWellington/Locksmith into SirWellington-feature/swift-2.0-update
* 'feature/swift-2.0-update' of https://github.com/SirWellington/Locksmith:
  + Updates to the new Swift 2 syntax
2015-06-23 07:32:29 +10:00
Juan Wellington Moreno 825d7d2485 + Updates to the new Swift 2 syntax 2015-06-20 14:22:43 -07:00
matthewpalmer 0418e37185 Release 1.2.2 2015-05-24 09:44:45 +10:00
Matthew Palmer c218c1fbea Add note on running tests 2015-05-24 09:34:49 +10:00
Matthew Palmer 68515bdc2c Merge pull request #34 from larslockefeer/CarthageSupport
Fixed some leftover Carthage issues
2015-05-24 09:05:42 +10:00
Lars Lockefeer f38046bb9e Added Info.plist file 2015-05-23 12:50:51 +02:00
Lars Lockefeer e804dd0452 Set deployment target to iOS 8.0 2015-05-22 17:52:26 +02:00
Matthew Palmer 04c6084a8f Merge pull request #33 from larslockefeer/CarthageSupport
Carthage support
2015-05-22 21:05:24 +10:00
Lars Lockefeer fbc549973b Made the xcode scheme shared for Carthage support 2015-05-21 18:30:19 +02:00
Lars Lockefeer 4ad7ca14e6 Made LocksmithDefaultService resilient against absent plist keys 2015-05-21 18:26:06 +02:00
Lars Lockefeer 582f74e305 Updated the tests
The signature of some methods in `Locksmith.swift` was updated, but the
tests weren’t. This commit updates the tests such that they resemble
the API exposed by `Locksmith.swift` again.
2015-05-21 18:25:38 +02:00
Lars Lockefeer 1758691bc2 Restored the project file
The main project file was still based on the Project Structure before
the addition of Cocoapods support. It has now been restored, such that
the project can be opened and compiled
2015-05-21 18:24:03 +02:00
Matthew Palmer 2a21d9d0d1 Merge pull request #30 from matthewpalmer/1.2.1
Update podspec and tests for latest version
2015-04-10 10:18:17 +10:00
matthewpalmer ca70968264 Update podspec and tests for latest version 2015-04-10 10:15:08 +10:00
Matthew Palmer 8017ad0d4e Merge pull request #29 from matthewpalmer/1.2.1
Update README and podspec for 1.2.1
2015-04-10 09:21:46 +10:00
matthewpalmer e77cffad00 Update README and podspec for 1.2.1 2015-04-10 09:18:45 +10:00
matthewpalmer 8d825ab368 Add Swift 1.2 support
Merge branch 'ChaosCoder-master'

* ChaosCoder-master:
  Converted to Swift 1.2
  Trying to fix travis builds
  removing comments
  public'ing all the things
  kSecAttrAccessible
  Update README.md
  Update README.md
  Remove false Travis badge
2015-04-10 08:53:54 +10:00
Andreas Ganske fb4030034d Converted to Swift 1.2 2015-02-11 22:51:53 +01:00
Matthew Palmer 8f21125a77 Trying to fix travis builds 2015-02-04 14:01:02 +11:00
Matthew Palmer 3d6ce4d5f0 Merge pull request #24 from marcelofabri/kSecAttrAccessible
Adding support for kSecAttrAccessible
2015-02-04 13:58:35 +11:00
Marcelo Fabri 4caf3e99b4 removing comments 2015-02-03 23:48:58 -02:00
Marcelo Fabri d04d2fe9b7 public'ing all the things 2015-02-03 23:48:33 -02:00
Marcelo Fabri 440de6f490 kSecAttrAccessible 2015-02-03 23:26:50 -02:00
Matthew Palmer 658233b596 Update README.md 2015-02-03 16:30:22 +11:00
Matthew Palmer 4246e19952 Update README.md 2015-01-28 21:13:33 +11:00
Matthew Palmer b988530bce Remove false Travis badge 2015-01-28 21:12:50 +11:00
Matthew Palmer 73d14afcfa Merge pull request #18 from matthewpalmer/1.2.0
1.2.0
2015-01-28 21:06:45 +11:00
matthewpalmer c8a14a87b0 Attempt to fix travis build 2015-01-28 21:04:18 +11:00
matthewpalmer 73074c7755 Remove old files
Cleanup for release
2015-01-28 21:02:08 +11:00
matthewpalmer 480c51f0bc Update pods to specify 1.2.0 2015-01-28 21:00:31 +11:00
matthewpalmer 780e9afe24 Pre push commit 2015-01-28 20:50:33 +11:00
matthewpalmer a64c1d9dc7 Updated to allow for default services.
Note that this commit comes after a gruelling battle with git and Xcode,
so stuff might get weird
2015-01-28 20:46:17 +11:00
Matthew Palmer 56ae2fb436 Update README.md 2015-01-25 21:59:23 +11:00
Matthew Palmer c8597b8c27 Update README.md 2015-01-25 21:56:04 +11:00
matthewpalmer cf322dcc1a Fixed local podspec version no. and remote url. don't know why this got
messed up
2015-01-25 20:58:54 +11:00
matthewpalmer 19955da055 Prep for cocoapods 2015-01-25 20:46:58 +11:00
matthewpalmer d172aac89a Removed old license 2015-01-25 20:37:01 +11:00
matthewpalmer b6b4f48b60 Fixed info.plist reference 2015-01-25 20:35:03 +11:00
matthewpalmer 0b5e1c085d Merge branch 'master' of https://github.com/matthewpalmer/Locksmith
* 'master' of https://github.com/matthewpalmer/Locksmith: (42 commits)
  Remove note on buggy release builds; fixed in #14
  replace unmanaged objects with withUnsafeMutablePointer to fix matthewpalmer/Locksmith#13
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Update README.markdown
  Added support for iCloud sync. Added support for different forms of security classes.
  Added basic tests
  Update README.markdown
  Removed requirement for the redundant 'key' argument
  Added method to clear the keychain
  Improve installation instructions
  Update README.markdown
  Update README.markdown
  Fix typo in readme
  ...

Conflicts:
	.gitignore
2015-01-25 20:29:45 +11:00
matthewpalmer fc6d06bc80 Updated Example to use latest pod version 2015-01-25 20:28:02 +11:00
matthewpalmer bdc30e42d4 Update podspec version number 2015-01-25 20:27:00 +11:00
matthewpalmer e1cae42bf0 Remove old reference in README.md 2015-01-25 20:23:55 +11:00
matthewpalmer 615cf5d375 Removed obj-c demo application 2015-01-25 20:22:47 +11:00
matthewpalmer 45795fc90b Added cocoapods package control 2015-01-25 20:21:50 +11:00
matthewpalmer 364679a64f Initial commit 2015-01-25 18:54:03 +11:00
Matthew Palmer a8d082ca0a Remove note on buggy release builds; fixed in #14 2015-01-20 19:21:44 +11:00
Matthew Palmer 7e9a292ace Merge pull request #14 from olvidalo/master
replace unmanaged objects with withUnsafeMutablePointer to fix matthewpa...
2015-01-20 19:20:20 +11:00
Marcel Schaeben 6d6480b5eb replace unmanaged objects with withUnsafeMutablePointer to fix matthewpalmer/Locksmith#13 2015-01-20 09:03:10 +01:00
Matthew Palmer df7724944b Update README.markdown 2015-01-06 18:41:29 +11:00
Matthew Palmer f823d2f166 Update README.markdown
Make argument types explicit
2015-01-04 12:20:16 +11:00
Matthew Palmer acd5f89a62 Update README.markdown 2014-12-24 18:23:16 +11:00
Matthew Palmer d2bde3b36e Update README.markdown
Improved formatting
2014-12-24 18:22:00 +11:00
Matthew Palmer 130773ac4d Update README.markdown 2014-12-24 17:36:48 +11:00
Matthew Palmer 9d6e0e420f Update README.markdown 2014-12-24 17:36:06 +11:00
Matthew Palmer 90b2c815f3 Update README.markdown 2014-12-24 17:35:09 +11:00
Matthew Palmer bf43e70035 Update README.markdown 2014-12-24 17:34:31 +11:00
Matthew Palmer 213a6231b8 Update README.markdown
Updated README to include new custom attributes.
2014-12-24 17:33:10 +11:00
matthewpalmer c06d1fbcfb Added support for iCloud sync.
Added support for different forms of security classes.
2014-12-24 17:26:06 +11:00
matthewpalmer a03582b242 Merge branch 'master' of https://github.com/matthewpalmer/Locksmith
* 'master' of https://github.com/matthewpalmer/Locksmith:
  Update README.markdown
2014-12-23 19:51:06 +11:00
matthewpalmer f9b125c00e Added basic tests 2014-12-23 19:50:34 +11:00
Matthew Palmer 17ede022a5 Update README.markdown
Remove references to 'key' argument, which is no longer needed.
2014-12-23 12:46:18 +11:00
matthewpalmer 883b340665 Removed requirement for the redundant 'key' argument 2014-12-23 12:42:07 +11:00
matthewpalmer dd4e8be44d Added method to clear the keychain 2014-12-23 12:29:51 +11:00
Matthew Palmer 9e20d2f4ec Improve installation instructions 2014-12-23 08:33:50 +11:00
Matthew Palmer 27a7e90721 Update README.markdown 2014-12-23 08:32:01 +11:00
Matthew Palmer d07cbdc7a1 Update README.markdown 2014-12-23 08:31:32 +11:00
matthewpalmer dde2b99101 Fix typo in readme 2014-12-23 08:29:39 +11:00
matthewpalmer 50e2cc5523 Update readme 2014-12-23 08:29:02 +11:00
Matthew Palmer 475d29c147 Merge pull request #9 from getcircle/master
Convert Locksmith to a framework
2014-12-23 07:39:47 +11:00
Michael Hahn 745158bf37 Make classes public so we can import them from the framework 2014-12-22 07:53:48 -08:00
Michael Hahn 6bec28e642 Update README.markdown 2014-12-22 07:45:53 -08:00
Michael Hahn acf0df75d0 Update installation docs 2014-12-22 07:44:50 -08:00
Michael Hahn fb0be8af55 Set the swift optimization level to Onone
per:
http://matthewpalmer.net/blog/2014/12/11/change-optimization-level-xcode-swift/
2014-12-22 07:44:10 -08:00
Michael Hahn 1f2a865abe Convert to a framework 2014-12-22 07:34:11 -08:00
matthewpalmer 075fd7800c Added note on optimization.
Changed flag for demo project
2014-12-11 06:58:27 +11:00
matthewpalmer e1b538eed9 Added note on optimization level 2014-12-11 06:53:06 +11:00
matthewpalmer d788b76f90 Add better error handling for certain requests. Fixes #7. 2014-12-09 07:14:59 +11:00
50 changed files with 4122 additions and 947 deletions
+26 -11
View File
@@ -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
View File
@@ -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 @@
Did you know that git does not support storing empty directories?
@@ -0,0 +1,11 @@
//
// ExtensionDelegate.swift
// Locksmith Extension
//
// Created by Tai Heng on 12/09/2015.
// Copyright © 2015 Matthew Palmer. All rights reserved.
//
import WatchKit
class ExtensionDelegate: NSObject, WKExtensionDelegate {}
@@ -0,0 +1,40 @@
<?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>CFBundleDisplayName</key>
<string>Locksmith Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>net.matthewpalmer.Locksmith-iOS-Example.watchkitapp</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>RemoteInterfacePrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).InterfaceController</string>
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
</dict>
</plist>
@@ -0,0 +1,51 @@
//
// InterfaceController.swift
// LocksmithExample WatchKit Extension
//
// Created by Tai Heng on 05/09/2015.
// Copyright © 2015 matthewpalmer. All rights reserved.
//
import WatchKit
import Foundation
import Locksmith
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
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("Watch app: \(result), \(result?.data)")
// DeleteableSecureStorable lets us delete the account from the keychain
try! account.deleteFromSecureStore()
}
}
@@ -0,0 +1,604 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
056F2A731BA42E3C00B24B65 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 056F2A711BA42E3C00B24B65 /* Interface.storyboard */; };
056F2A751BA42E3C00B24B65 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 056F2A741BA42E3C00B24B65 /* Assets.xcassets */; };
056F2A7C1BA42E3C00B24B65 /* Locksmith Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 056F2A7B1BA42E3C00B24B65 /* Locksmith Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
056F2A811BA42E3C00B24B65 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 056F2A801BA42E3C00B24B65 /* InterfaceController.swift */; };
056F2A831BA42E3C00B24B65 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 056F2A821BA42E3C00B24B65 /* ExtensionDelegate.swift */; };
056F2A851BA42E3C00B24B65 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 056F2A841BA42E3C00B24B65 /* Assets.xcassets */; };
056F2A891BA42E3C00B24B65 /* Locksmith.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 056F2A6F1BA42E3C00B24B65 /* Locksmith.app */; };
056F2A941BA42FFC00B24B65 /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 056F2A931BA42FFC00B24B65 /* Locksmith.framework */; };
056F2A961BA4300700B24B65 /* Locksmith.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 056F2A951BA4300700B24B65 /* Locksmith.framework */; };
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 */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
056F2A7D1BA42E3C00B24B65 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0E13A9981BA3EE8600A06FF9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 056F2A7A1BA42E3C00B24B65;
remoteInfo = "Locksmith Extension";
};
056F2A871BA42E3C00B24B65 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0E13A9981BA3EE8600A06FF9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 056F2A6E1BA42E3C00B24B65;
remoteInfo = Locksmith;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
056F2A8F1BA42E3C00B24B65 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
056F2A7C1BA42E3C00B24B65 /* Locksmith Extension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
056F2A911BA42E3C00B24B65 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
056F2A891BA42E3C00B24B65 /* Locksmith.app in Embed Watch Content */,
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
0E13A9BA1BA3F03D00A06FF9 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
056F2A6F1BA42E3C00B24B65 /* Locksmith.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locksmith.app; sourceTree = BUILT_PRODUCTS_DIR; };
056F2A721BA42E3C00B24B65 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
056F2A741BA42E3C00B24B65 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
056F2A761BA42E3C00B24B65 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
056F2A7B1BA42E3C00B24B65 /* Locksmith Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Locksmith Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
056F2A801BA42E3C00B24B65 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = "<group>"; };
056F2A821BA42E3C00B24B65 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = "<group>"; };
056F2A841BA42E3C00B24B65 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
056F2A861BA42E3C00B24B65 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
056F2A931BA42FFC00B24B65 /* Locksmith.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Locksmith.framework; path = "../../build/Debug-iphoneos/Locksmith.framework"; sourceTree = "<group>"; };
056F2A951BA4300700B24B65 /* Locksmith.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Locksmith.framework; path = "../../build/Debug-watchos/Locksmith.framework"; sourceTree = "<group>"; };
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>"; };
15FCF62F6935D11C671F41FF /* Pods_app.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_app.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1D62CFE6D2814693AE03CF5C /* Pods_watch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_watch.framework; sourceTree = BUILT_PRODUCTS_DIR; };
379DD1E5C8CDE5ABD6E50A38 /* Pods-watch.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watch.release.xcconfig"; path = "Pods/Target Support Files/Pods-watch/Pods-watch.release.xcconfig"; sourceTree = "<group>"; };
74E67F961D2CBE4F8D171223 /* Pods-watch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watch.debug.xcconfig"; path = "Pods/Target Support Files/Pods-watch/Pods-watch.debug.xcconfig"; sourceTree = "<group>"; };
8D5C601685F38F2EA6AA2CA0 /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Pods/Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
FFB6EBF2507E751B53E55514 /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Pods/Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
056F2A781BA42E3C00B24B65 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
056F2A961BA4300700B24B65 /* Locksmith.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0E13A99D1BA3EE8700A06FF9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
056F2A941BA42FFC00B24B65 /* Locksmith.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
056F2A701BA42E3C00B24B65 /* Locksmith */ = {
isa = PBXGroup;
children = (
056F2A711BA42E3C00B24B65 /* Interface.storyboard */,
056F2A741BA42E3C00B24B65 /* Assets.xcassets */,
056F2A761BA42E3C00B24B65 /* Info.plist */,
);
path = Locksmith;
sourceTree = "<group>";
};
056F2A7F1BA42E3C00B24B65 /* Locksmith Extension */ = {
isa = PBXGroup;
children = (
056F2A801BA42E3C00B24B65 /* InterfaceController.swift */,
056F2A821BA42E3C00B24B65 /* ExtensionDelegate.swift */,
056F2A841BA42E3C00B24B65 /* Assets.xcassets */,
056F2A861BA42E3C00B24B65 /* Info.plist */,
);
path = "Locksmith Extension";
sourceTree = "<group>";
};
0E13A9971BA3EE8600A06FF9 = {
isa = PBXGroup;
children = (
0E13A9A21BA3EE8700A06FF9 /* Locksmith iOS Example */,
056F2A701BA42E3C00B24B65 /* Locksmith */,
056F2A7F1BA42E3C00B24B65 /* Locksmith Extension */,
0E13A9A11BA3EE8700A06FF9 /* Products */,
74B88A8534E794C2027D6B3D /* Pods */,
D1E3110F9ED1A0EE737E7A37 /* Frameworks */,
);
sourceTree = "<group>";
};
0E13A9A11BA3EE8700A06FF9 /* Products */ = {
isa = PBXGroup;
children = (
0E13A9A01BA3EE8700A06FF9 /* Locksmith iOS Example.app */,
056F2A6F1BA42E3C00B24B65 /* Locksmith.app */,
056F2A7B1BA42E3C00B24B65 /* Locksmith Extension.appex */,
);
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>";
};
74B88A8534E794C2027D6B3D /* Pods */ = {
isa = PBXGroup;
children = (
FFB6EBF2507E751B53E55514 /* Pods-app.debug.xcconfig */,
8D5C601685F38F2EA6AA2CA0 /* Pods-app.release.xcconfig */,
74E67F961D2CBE4F8D171223 /* Pods-watch.debug.xcconfig */,
379DD1E5C8CDE5ABD6E50A38 /* Pods-watch.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
D1E3110F9ED1A0EE737E7A37 /* Frameworks */ = {
isa = PBXGroup;
children = (
056F2A951BA4300700B24B65 /* Locksmith.framework */,
056F2A931BA42FFC00B24B65 /* Locksmith.framework */,
15FCF62F6935D11C671F41FF /* Pods_app.framework */,
1D62CFE6D2814693AE03CF5C /* Pods_watch.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
056F2A6E1BA42E3C00B24B65 /* Locksmith */ = {
isa = PBXNativeTarget;
buildConfigurationList = 056F2A901BA42E3C00B24B65 /* Build configuration list for PBXNativeTarget "Locksmith" */;
buildPhases = (
056F2A6D1BA42E3C00B24B65 /* Resources */,
056F2A8F1BA42E3C00B24B65 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
056F2A7E1BA42E3C00B24B65 /* PBXTargetDependency */,
);
name = Locksmith;
productName = Locksmith;
productReference = 056F2A6F1BA42E3C00B24B65 /* Locksmith.app */;
productType = "com.apple.product-type.application.watchapp2";
};
056F2A7A1BA42E3C00B24B65 /* Locksmith Extension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 056F2A8E1BA42E3C00B24B65 /* Build configuration list for PBXNativeTarget "Locksmith Extension" */;
buildPhases = (
056F2A771BA42E3C00B24B65 /* Sources */,
056F2A781BA42E3C00B24B65 /* Frameworks */,
056F2A791BA42E3C00B24B65 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Locksmith Extension";
productName = "Locksmith Extension";
productReference = 056F2A7B1BA42E3C00B24B65 /* Locksmith Extension.appex */;
productType = "com.apple.product-type.watchkit2-extension";
};
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 */,
056F2A911BA42E3C00B24B65 /* Embed Watch Content */,
);
buildRules = (
);
dependencies = (
056F2A881BA42E3C00B24B65 /* PBXTargetDependency */,
);
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 = {
056F2A6E1BA42E3C00B24B65 = {
CreatedOnToolsVersion = 7.0;
};
056F2A7A1BA42E3C00B24B65 = {
CreatedOnToolsVersion = 7.0;
};
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 */,
056F2A6E1BA42E3C00B24B65 /* Locksmith */,
056F2A7A1BA42E3C00B24B65 /* Locksmith Extension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
056F2A6D1BA42E3C00B24B65 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
056F2A751BA42E3C00B24B65 /* Assets.xcassets in Resources */,
056F2A731BA42E3C00B24B65 /* Interface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
056F2A791BA42E3C00B24B65 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
056F2A851BA42E3C00B24B65 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
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 */
056F2A771BA42E3C00B24B65 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
056F2A831BA42E3C00B24B65 /* ExtensionDelegate.swift in Sources */,
056F2A811BA42E3C00B24B65 /* InterfaceController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0E13A99C1BA3EE8700A06FF9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0E13A9A61BA3EE8700A06FF9 /* ViewController.swift in Sources */,
0E13A9A41BA3EE8700A06FF9 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
056F2A7E1BA42E3C00B24B65 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 056F2A7A1BA42E3C00B24B65 /* Locksmith Extension */;
targetProxy = 056F2A7D1BA42E3C00B24B65 /* PBXContainerItemProxy */;
};
056F2A881BA42E3C00B24B65 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 056F2A6E1BA42E3C00B24B65 /* Locksmith */;
targetProxy = 056F2A871BA42E3C00B24B65 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
056F2A711BA42E3C00B24B65 /* Interface.storyboard */ = {
isa = PBXVariantGroup;
children = (
056F2A721BA42E3C00B24B65 /* Base */,
);
name = Interface.storyboard;
sourceTree = "<group>";
};
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 */
056F2A8A1BA42E3C00B24B65 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
IBSC_MODULE = Locksmith_Extension;
INFOPLIST_FILE = Locksmith/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example.watchkitapp";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Debug;
};
056F2A8B1BA42E3C00B24B65 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
IBSC_MODULE = Locksmith_Extension;
INFOPLIST_FILE = Locksmith/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example.watchkitapp";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Release;
};
056F2A8C1BA42E3C00B24B65 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 74E67F961D2CBE4F8D171223 /* Pods-watch.debug.xcconfig */;
buildSettings = {
INFOPLIST_FILE = "Locksmith Extension/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example.watchkitapp.watchkitextension";
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Debug;
};
056F2A8D1BA42E3C00B24B65 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 379DD1E5C8CDE5ABD6E50A38 /* Pods-watch.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = "Locksmith Extension/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.matthewpalmer.Locksmith-iOS-Example.watchkitapp.watchkitextension";
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Release;
};
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;
baseConfigurationReference = FFB6EBF2507E751B53E55514 /* Pods-app.debug.xcconfig */;
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;
baseConfigurationReference = 8D5C601685F38F2EA6AA2CA0 /* Pods-app.release.xcconfig */;
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 */
056F2A8E1BA42E3C00B24B65 /* Build configuration list for PBXNativeTarget "Locksmith Extension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
056F2A8C1BA42E3C00B24B65 /* Debug */,
056F2A8D1BA42E3C00B24B65 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
056F2A901BA42E3C00B24B65 /* Build configuration list for PBXNativeTarget "Locksmith" */ = {
isa = XCConfigurationList;
buildConfigurations = (
056F2A8A1BA42E3C00B24B65 /* Debug */,
056F2A8B1BA42E3C00B24B65 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
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 */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
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,47 @@
//
// 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("iOS app: \(result), \(result?.data)")
// DeleteableSecureStorable lets us delete the account from the keychain
try! account.deleteFromSecureStore()
return true
}
}
@@ -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,27 @@
<?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="kvZ-1V-3wx">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8142"/>
</dependencies>
<scenes>
<!--Locksmith Example-->
<scene sceneID="DFV-sl-Fly">
<objects>
<viewController title="Locksmith Example" id="kvZ-1V-3wx" customClass="ViewController" customModule="Locksmith_iOS_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Jdi-8X-X7l"/>
<viewControllerLayoutGuide type="bottom" id="xfR-hD-RHw"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="tK0-bv-krV">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zND-74-PgH" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="347" y="447"/>
</scene>
</scenes>
</document>
@@ -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 {}
@@ -0,0 +1,62 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"scale" : "2x",
"role" : "longLook",
"subtype" : "42mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="8173.3" systemVersion="14F27" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="AgC-eL-Hgc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8142"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="8089"/>
</dependencies>
<scenes>
<!--Interface Controller-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller id="AgC-eL-Hgc" customClass="InterfaceController" customModule="Locksmith" customModuleProvider="target">
<items>
<label width="136" alignment="left" text="Locksmith" id="0hb-l0-xFa"/>
</items>
</controller>
</objects>
<point key="canvasLocation" x="220" y="345"/>
</scene>
<!--Static Notification Interface Controller-->
<scene sceneID="AEw-b0-oYE">
<objects>
<notificationController id="YCC-NB-fut">
<items>
<label alignment="left" text="Alert Label" id="IdU-wH-bcW"/>
</items>
<notificationCategory key="notificationCategory" identifier="myCategory" id="JfB-70-Muf"/>
<connections>
<outlet property="notificationAlertLabel" destination="IdU-wH-bcW" id="JKC-fr-R95"/>
<segue destination="4sK-HA-Art" kind="relationship" relationship="dynamicNotificationInterface" id="kXh-Jw-8B1"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="220" y="643"/>
</scene>
<!--Notification Controller-->
<scene sceneID="ZPc-GJ-vnh">
<objects>
<controller id="4sK-HA-Art" customClass="NotificationController" customModule="Locksmith" customModuleProvider="target"/>
</objects>
<point key="canvasLocation" x="468" y="643"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,35 @@
<?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>CFBundleDisplayName</key>
<string>Locksmith iOS Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>net.matthewpalmer.Locksmith-iOS-Example</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>
+5 -7
View File
@@ -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.
+24
View File
@@ -0,0 +1,24 @@
Pod::Spec.new do |s|
s.name = "Locksmith"
s.version = "2.0.8"
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.tvos.deployment_target = '9.0'
s.requires_arc = true
s.source_files = 'Source/*.{m,h,swift}'
s.frameworks = 'Foundation', 'Security'
end
File diff suppressed because it is too large Load Diff
@@ -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>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0610"
LastUpgradeVersion = "0720"
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 = "0720"
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 = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FBD0C9481C1866BE00291F2A"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith tvOS"
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 = "FBD0C9481C1866BE00291F2A"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith tvOS"
ReferencedContainer = "container:Locksmith.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FBD0C9481C1866BE00291F2A"
BuildableName = "Locksmith.framework"
BlueprintName = "Locksmith tvOS"
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 = "0720"
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>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>
@@ -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
View File
@@ -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>
-46
View File
@@ -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:.
}
}
-41
View File
@@ -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>
-73
View File
@@ -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>
-236
View File
@@ -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)
}
}
}
-58
View File
@@ -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
}
}
-52
View File
@@ -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.
}
}
-36
View File
@@ -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.
}
}
}
-71
View File
@@ -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
```
+465
View File
@@ -0,0 +1,465 @@
# 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
- [x] 📺 tvOS
> &nbsp;
>
> I make [Rocket](http://matthewpalmer.net/rocket?utm_source=locksmith&utm_medium=readme&utm_campaign=open_source), an app that gives you Slack-style emoji everywhere on your Mac.
>
> &nbsp;
## Details
How is Locksmith different to other keychain wrappers?
* Locksmiths API is both super-simple and deeply powerful
* Provides access to all of the keychains metadata with strongly typed results
* Add functionality to your existing types for free
* Useful enums and Swift-native types
> Want to read more about Locksmiths 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
[![Version](https://img.shields.io/cocoapods/v/Locksmith.svg?style=flat)](http://cocoadocs.org/docsets/Locksmith)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Build Status](https://travis-ci.org/matthewpalmer/Locksmith.svg?branch=master)](https://travis-ci.org/matthewpalmer/Locksmith)
### CocoaPods
Locksmith is available through [CocoaPods](http://cocoapods.org).
pod 'Locksmith'
### Carthage
Locksmith is available through [Carthage](https://github.com/Carthage/Carthage).
github "matthewpalmer/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 youve gotta do,” and “whats 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 its 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—its 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 cant 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!
Lets start with an example: the Twitter account from before, except its 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 lets 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. [Apples documentation](https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/#//apple_ref/c/econst/errSecSuccess) provides more information on these errors.
```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`. [Apples documentation](https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/#//apple_ref/doc/constant_group/Authentication_Type_Values) provides more information on these values.
```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. [Apples documentation](https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/#//apple_ref/doc/constant_group/Protocol_Values) provides more information on these values.
```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.
+31
View File
@@ -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
}
}
}
+26
View File
@@ -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>2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
+4
View File
@@ -0,0 +1,4 @@
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT double LocksmithVersionNumber;
FOUNDATION_EXPORT const unsigned char LocksmithVersionString[];
+629
View File
@@ -0,0 +1,629 @@
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 {
struct UpdateRequest: GenericPasswordSecureStorable, CreateableSecureStorable {
let service: String
let account: String
let data: [String: AnyObject]
}
let request = UpdateRequest(service: service, account: userAccount, data: data)
try request.updateInSecureStore()
}
}
// 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
func updateInSecureStore() 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 {
if let result = try performSecureStorageAction(performReadRequestClosure, secureStoragePropertyDictionary: asReadableSecureStoragePropertyDictionary) {
return GenericPasswordResult(resultDictionary: result)
} else {
return nil
}
} catch {
return nil
}
}
}
public extension ReadableSecureStorable where Self : InternetPasswordSecureStorable {
func readFromSecureStore() -> InternetPasswordSecureStorableResultType? {
do {
if let result = try performSecureStorageAction(performReadRequestClosure, secureStoragePropertyDictionary: asReadableSecureStoragePropertyDictionary) {
return InternetPasswordResult(resultDictionary: result)
} else {
return nil
}
} catch {
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
extension CreateableSecureStorable {
func updateInSecureStore(query: [String: AnyObject]) throws {
var attributesToUpdate = query
attributesToUpdate[String(kSecClass)] = nil
let status = SecItemUpdate(query, attributesToUpdate)
if let error = LocksmithError(fromStatusCode: Int(status)) {
if error == .NotFound || error == .NotAvailable {
try self.createInSecureStore()
} else {
throw error
}
} else {
if status != errSecSuccess {
throw LocksmithError.Undefined
}
}
}
}
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)
}
func updateInSecureStore() throws {
try self.updateInSecureStore(self.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)
}
func updateInSecureStore() throws {
try self.updateInSecureStore(self.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]
}
}
+46
View File
@@ -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)
}
}
}
+44
View File
@@ -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)
}
}
}
+141
View File
@@ -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)
}
}
}
+39
View File
@@ -0,0 +1,39 @@
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:
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)
}
}
}
+466
View File
@@ -0,0 +1,466 @@
//
// 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)
try! Locksmith.deleteDataForUserAccount(userAccount, inService: service)
try! Locksmith.updateData(["some update": "data"], forUserAccount: userAccount, inService: service)
let updateResult = Locksmith.loadDataForUserAccount(userAccount, inService: service)! as! [String: String]
XCTAssertEqual(updateResult, ["some update": "data"])
}
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 testUpdateCreatesIfNotExists() {
let data = ["some": "data"]
struct CreateGenericPassword: CreateableSecureStorable, GenericPasswordSecureStorable, ReadableSecureStorable {
var data: [String: AnyObject]
let account: String
let service: String
}
let update = CreateGenericPassword(data: data, account: userAccount, service: service)
try! update.updateInSecureStore()
let read = update.readFromSecureStore()!.data as! [String: String]
XCTAssertEqual(read, ["some": "data"])
}
func testUpdateForGenericPassword() {
let data = ["some": "data"]
struct CreateGenericPassword: CreateableSecureStorable, GenericPasswordSecureStorable, ReadableSecureStorable {
var data: [String: AnyObject]
let account: String
let service: String
}
var create = CreateGenericPassword(data: data, account: userAccount, service: service)
try! create.createInSecureStore() // make sure it doesn't throw
create.data = ["other": "data"]
try! create.updateInSecureStore()
let read = create.readFromSecureStore()!.data as! [String: String]
XCTAssertEqual(read, ["other": "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
var 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
}
var c = CreateInternetPassword(account: userAccount, data: initialData, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType, path: path, securityDomain: securityDomain)
try! c.createInSecureStore()
func assertResultMetadataIsOk(result: InternetPasswordSecureStorableResultType?) {
XCTAssertEqual(result?.account, userAccount)
XCTAssertEqual(result?.server, server)
XCTAssertEqual(result?.port, port)
XCTAssertEqual(result?.internetProtocol, internetProtocol)
XCTAssertEqual(result?.authenticationType, authenticationType)
XCTAssertEqual(result?.securityDomain, securityDomain)
XCTAssertEqual(result?.path, path)
}
let r = ReadInternetPassword(account: userAccount, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
let result = r.readFromSecureStore()
XCTAssertEqual(result!.data as! [String: String], initialData)
assertResultMetadataIsOk(result)
// Assert that metadata is maintained after an update
c.data = ["other internet": "junk"]
try! c.updateInSecureStore()
let result2 = r.readFromSecureStore()
XCTAssertEqual(result2!.data as! [String: String], ["other internet": "junk"])
assertResultMetadataIsOk(result2)
}
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)
}
}