├── .gitignore
├── .travis.yml
├── Debug.entitlements
├── LICENSE
├── Package.swift
├── README.md
├── SwiftKeychainWrapper.podspec
├── SwiftKeychainWrapper.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ └── SwiftKeychainWrapper.xcscheme
├── SwiftKeychainWrapper
├── Info.plist
├── KeychainItemAccessibility.swift
├── KeychainWrapper.swift
├── KeychainWrapperSubscript.swift
└── SwiftKeychainWrapper.h
├── SwiftKeychainWrapperTests
├── Info.plist
├── KeychainWrapperDefaultWrapperTests.swift
├── KeychainWrapperDeleteTests.swift
├── KeychainWrapperPrimitiveValueTests.swift
├── KeychainWrapperTests.swift
└── TestObject.swift
└── TestHostApp
├── AppDelegate.swift
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Debug.entitlements
├── Info.plist
└── ViewController.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 | .swiftpm/
20 |
21 | # CocoaPods
22 | #
23 | # We recommend against adding the Pods directory to your .gitignore. However
24 | # you should judge for yourself, the pros and cons are mentioned at:
25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
26 | #
27 | # Pods/
28 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode10.2
3 |
4 | script:
5 | - xcodebuild test -scheme SwiftKeychainWrapper -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone SE,OS=12.1"
6 | # - xcodebuild test -scheme SwiftKeychainWrapper -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV,OS=12.1"
7 |
--------------------------------------------------------------------------------
/Debug.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | get-task-allow
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Jason Rendel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.1
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "SwiftKeychainWrapper",
7 | products: [
8 | .library(name: "SwiftKeychainWrapper", targets: ["SwiftKeychainWrapper"])
9 | ],
10 | targets: [
11 | .target(name: "SwiftKeychainWrapper", path: "SwiftKeychainWrapper"),
12 | .testTarget(name: "SwiftKeychainWrapperTests", dependencies: ["SwiftKeychainWrapper"], path: "SwiftKeychainWrapperTests")
13 | ]
14 | )
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :no_entry: [DEPRECATED] This library is not being kept up to date. This was created when Swift was first released as a simple way to work with the Keychain. But the approach was restricted and not suited to more advanced keychain usage.
2 |
3 | # SwiftKeychainWrapper
4 |
5 | A simple wrapper for the iOS / tvOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift.
6 |
7 | Provides singleton instance that is setup to work for most needs. Use `KeychainWrapper.standard` to access the singleton instance.
8 |
9 | If you need to customize the keychain access to use a custom identifier or access group, you can create your own instance instead of using the singleton instance.
10 |
11 | By default, the Keychain Wrapper saves data as a Generic Password type in the iOS Keychain. It saves items such that they can only be accessed when the app is unlocked and open. If you are not familiar with the iOS Keychain usage, this provides a safe default for using the keychain.
12 |
13 | Users that want to deviate from this default implementation, now can do so in version 2.0 and up. Each request to save/read a key value now allows you to specify the keychain accessibility for that key.
14 |
15 | ## General Usage
16 |
17 | Add a string value to keychain:
18 | ``` swift
19 | let saveSuccessful: Bool = KeychainWrapper.standard.set("Some String", forKey: "myKey")
20 | ```
21 |
22 | Retrieve a string value from keychain:
23 | ``` swift
24 | let retrievedString: String? = KeychainWrapper.standard.string(forKey: "myKey")
25 | ```
26 |
27 | Remove a string value from keychain:
28 | ``` swift
29 | let removeSuccessful: Bool = KeychainWrapper.standard.removeObject(forKey: "myKey")
30 | ```
31 |
32 | ## Custom Instance
33 |
34 | When the Keychain Wrapper is used, all keys are linked to a common identifier for your app, called the service name. By default this uses your main bundle identifier. However, you may also change it, or store multiple items to the keychain under different identifiers.
35 |
36 | To share keychain items between your applications, you may specify an access group and use that same access group in each application.
37 |
38 | To set a custom service name identifier or access group, you may now create your own instance of the keychain wrapper as follows:
39 |
40 | ``` swift
41 | let uniqueServiceName = "customServiceName"
42 | let uniqueAccessGroup = "sharedAccessGroupName"
43 | let customKeychainWrapperInstance = KeychainWrapper(serviceName: uniqueServiceName, accessGroup: uniqueAccessGroup)
44 | ```
45 | The custom instance can then be used in place of the shared instance or static accessors:
46 |
47 | ``` swift
48 | let saveSuccessful: Bool = customKeychainWrapperInstance.set("Some String", forKey: "myKey")
49 |
50 | let retrievedString: String? = customKeychainWrapperInstance.string(forKey: "myKey")
51 |
52 | let removeSuccessful: Bool = customKeychainWrapperInstance.removeObject(forKey: "myKey")
53 | ```
54 |
55 | ## Subscript usage
56 |
57 | Keychain can also be accessed with subscript as it is in dictionary. Keys can be predefined and listed in one place for convenience.
58 |
59 | Firstly, let's define the key to use later.
60 |
61 | ``` swift
62 | extension KeychainWrapper.Key {
63 | static let myKey: KeychainWrapper.Key = "myKey"
64 | }
65 | ```
66 |
67 | And now we can use this key as follows:
68 |
69 | ``` swift
70 | KeychainWrapper.standard[.myKey] = "some string"
71 |
72 | let myValue: String? = KeychainWrapper.standard[.myKey]
73 |
74 | KeychainWrapper.standard.remove(forKey: .myKey)
75 |
76 | ```
77 |
78 |
79 | ## Accessibility Options
80 |
81 | By default, all items saved to keychain can only be accessed when the device is unlocked. To change this accessibility, an optional `withAccessibility` param can be set on all requests. The enum `KeychainItemAccessibilty` provides an easy way to select the accessibility level desired:
82 |
83 | ``` swift
84 | KeychainWrapper.standard.set("Some String", forKey: "myKey", withAccessibility: .AfterFirstUnlock)
85 | ```
86 |
87 | ## Synchronizable Option
88 |
89 | By default, all items saved to keychain are not synchronizable, so they are not synced with the iCloud. To change this, an `isSynchronizable` bool param can be set on all requests. You need the item to be synchronized with the iCloud if you want to have it on all of your devices:
90 |
91 | ``` swift
92 | KeychainWrapper.standard.set("Some String", forKey: "myKey", isSynchronizable: true)
93 | ```
94 |
95 | **Important:** You can't modify value for key if it was previously set with different accessibility option. Remove the value for key and set it with new accessibility option. (Otherwise the value will not change).
96 | For example:
97 | ``` swift
98 | KeychainWrapper.standard.set("String one", forKey: "myKey", withAccessibility: .AfterFirstUnlock)
99 | KeychainWrapper.standard.removeObject(forKey: "myKey")
100 | KeychainWrapper.standard.set("String two", forKey: "myKey", withAccessibility: .Always)
101 | ```
102 |
103 | ## Installation
104 |
105 | #### CocoaPods
106 | You can use [CocoaPods](http://cocoapods.org/) to install SwiftKeychainWrapper by adding it to your `Podfile`:
107 |
108 | ``` ruby
109 | use_frameworks!
110 | platform :ios, '8.0'
111 |
112 | target 'target_name' do
113 | pod 'SwiftKeychainWrapper'
114 | end
115 | ```
116 |
117 | To use the keychain wrapper in your app, import SwiftKeychainWrapper into the file(s) where you want to use it.
118 |
119 | ``` swift
120 | import SwiftKeychainWrapper
121 | ```
122 |
123 | #### Carthage
124 | You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftKeychainWrapper by adding it to your `Cartfile`.
125 |
126 | Swift 3.0:
127 | ```
128 | github "jrendel/SwiftKeychainWrapper" ~> 3.0
129 | ```
130 |
131 | Swift 2.3:
132 | ```
133 | github "jrendel/SwiftKeychainWrapper" == 2.1.1
134 | ```
135 |
136 | #### Swift Package Manager
137 | You can use [Swift Package Manager](https://swift.org/package-manager/) to install SwiftKeychainWrapper using Xcode:
138 |
139 | 1. Open your project in Xcode
140 |
141 | 2. Click "File" -> "Swift Packages" -> "Add Package Dependency..."
142 |
143 | 3. Paste the following URL: https://github.com/jrendel/SwiftKeychainWrapper
144 |
145 | 4. Click "Next" -> "Next" -> "Finish"
146 |
147 |
148 | #### Manually
149 | Download and drop ```KeychainWrapper.swift``` and ```KeychainItemAcessibility.swift``` into your project.
150 |
151 |
152 | ## Release History
153 |
154 | * 4.1
155 | Added conditional logic for CGFloat accessories for when package is used where CGFloat is not available
156 |
157 | * 4.0
158 | Updated with SPM support and other community PRs. Minimum iOS version is now 9.0.
159 |
160 | * 3.4
161 | * Changed how Swift version is defined for CocoaPods
162 |
163 | * 3.3
164 | * Updates for Swift 5.0 and Xcode 10.2
165 |
166 | * 3.2
167 | * Updates for Swift 4.2 and Xcode 10
168 |
169 | * 3.1
170 | * Updates for Swift 3.1
171 |
172 | * 3.0.1
173 | * Added a host app for the unit tests to get around the issue with keychain access not working the same on iOS 10 simulators
174 | * Minor update to readme instructions
175 |
176 | * 3.0
177 | * Swift 3.0 update. Contains breaking API changes. 2.2.0 and 2.2.1 are now rolled into 3.0
178 |
179 | * 2.2.1 (Removed from Cocoapods)
180 | * Syntax updates to be more Swift 3 like
181 |
182 | * 2.2 (Removed from Cocoapods)
183 | * Updated to support Swift 3.0
184 | * Remove deprecated functions (static access)
185 |
186 | * 2.1
187 | * Updated to support Swift 2.3
188 |
189 | * 2.0
190 | * Further changes to more closely align the API with how `NSUserDefaults` works. Access to the default implementation is now done through a singleton instance. Static accessors have been included that wrap this shared instance to maintain backwards compatibility. These will be removed in the next update
191 | * Ability to change keychain service name identifier and access group on the shared instance has been deprecated. Users now have the ability to create their own instance of the keychain if they want to customize these.
192 | * Addtional options have been provided to alter the keychain accessibility for each key value saved.
193 |
194 | * 1.0.11
195 | * Update for Swift 2.0
196 |
197 | * 1.0.10
198 | * Update License info. Merged Pull Request with Carthage support.
199 |
200 | * 1.0.8
201 | * Update for Swift 1.2
202 |
203 | * 1.0.7
204 | * Determined that once provisioned correctly for access groups, using KeychainWrapper on the simulator with access groups works. So I removed the simulator related check and unit tests previously added.
205 |
206 | * 1.0.6
207 | * Support for Access Groups
208 | * SwiftKeychainWrapperExample has been updated to show usage with an Access Group: https://github.com/jrendel/SwiftKeychainWrapperExample
209 |
210 | * Access Groups do not work on the simulator. Apps that are built for the simulator aren't signed, so there's no keychain access group for the simulator to check. This means that all apps can see all keychain items when run on the simulator. Attempting to set an access group will result in a failure when attempting to Add or Update keychain items. Because of this, the Keychain Wrapper detects if it is being using on a simulator and will not set an access group property if one is set. This allows the Keychain Wrapper to still be used on the simulator for development of your app. To properly test Keychain Access Groups, you will need to test on a device.
211 |
212 | * 1.0.5
213 | * This version converts the project to a proper Swift Framework and adds a podspec file to be compatible with the latest CocoaPods pre-release, which now supports Swift.
214 |
215 | * To see an example of usage with CocoaPods, I've created the repo SwiftKeychainWrapperExample: https://github.com/jrendel/SwiftKeychainWrapperExample
216 |
217 | * 1.0.2
218 | * Updated for Xcode 6.1
219 |
220 | ---
221 |
222 | I've been using an Objective-C based wrapper in my own projects for the past couple years. The original library I wrote for myself was based on the following tutorial:
223 |
224 | http://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1
225 |
226 | This is a rewrite of that code in Swift.
227 |
228 | [](https://github.com/Carthage/Carthage)
229 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SwiftKeychainWrapper'
3 | s.version = '4.0.1'
4 | s.summary = 'Wrapper for the iOS Keychain written in Swift.'
5 | s.description = <<-DESC
6 | A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to UserDefaults. Supports Access Groups. Written in Swift.'
7 | DESC
8 | s.module_name = "SwiftKeychainWrapper"
9 | s.homepage = 'https://github.com/jrendel/SwiftKeychainWrapper'
10 | s.license = 'MIT'
11 | s.authors = { 'Jason Rendel' => 'jason@jasonrendel.com' }
12 | s.ios.deployment_target = '9.0'
13 | s.tvos.deployment_target = '11.0'
14 | s.swift_version = ['4.2', '5.0']
15 | s.source = { :git => 'https://github.com/jrendel/SwiftKeychainWrapper.git', :tag => s.version }
16 | s.source_files = 'SwiftKeychainWrapper/*.{h,swift}'
17 | end
18 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1AC6BBDA1CCD272C00DE429B /* KeychainItemAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6BBD91CCD272C00DE429B /* KeychainItemAccessibility.swift */; };
11 | 470A7D3B1CCE40EE001768EE /* KeychainWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470A7D3A1CCE40EE001768EE /* KeychainWrapperTests.swift */; };
12 | 477728B91CAF29E500905FE4 /* KeychainWrapperPrimitiveValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 477728B81CAF29E500905FE4 /* KeychainWrapperPrimitiveValueTests.swift */; };
13 | 47E429C91DCCE2FD002BE498 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E429C81DCCE2FD002BE498 /* AppDelegate.swift */; };
14 | 47E429CB1DCCE2FD002BE498 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E429CA1DCCE2FD002BE498 /* ViewController.swift */; };
15 | 47E429CE1DCCE2FD002BE498 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 47E429CC1DCCE2FD002BE498 /* Main.storyboard */; };
16 | 47E429D01DCCE2FD002BE498 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 47E429CF1DCCE2FD002BE498 /* Assets.xcassets */; };
17 | 47E429D31DCCE2FD002BE498 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 47E429D11DCCE2FD002BE498 /* LaunchScreen.storyboard */; };
18 | 5E79BF2F246847600035CAC0 /* KeychainWrapperSubscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E79BF2E246847600035CAC0 /* KeychainWrapperSubscript.swift */; };
19 | 96405CF41CA57F460080FF45 /* KeychainWrapperDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96405CF31CA57F460080FF45 /* KeychainWrapperDeleteTests.swift */; };
20 | 966177401D597872000772C5 /* KeychainWrapperDefaultWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9661773F1D597872000772C5 /* KeychainWrapperDefaultWrapperTests.swift */; };
21 | 966E4AB01B96693F00A8DCEB /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 966E4AAF1B96693F00A8DCEB /* LICENSE */; };
22 | 9694C2B51A66097F005B3030 /* SwiftKeychainWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 9694C2B41A66097F005B3030 /* SwiftKeychainWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
23 | 9694C2BB1A66097F005B3030 /* SwiftKeychainWrapper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9694C2B01A66097F005B3030 /* SwiftKeychainWrapper.framework */; };
24 | 9694C2CD1A660F90005B3030 /* KeychainWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9694C2CC1A660F90005B3030 /* KeychainWrapper.swift */; };
25 | 9694C2D11A660F9C005B3030 /* TestObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9694C2CF1A660F9C005B3030 /* TestObject.swift */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXContainerItemProxy section */
29 | 47E429D91DCCE408002BE498 /* PBXContainerItemProxy */ = {
30 | isa = PBXContainerItemProxy;
31 | containerPortal = 961101BC19D2581700E6A6E3 /* Project object */;
32 | proxyType = 1;
33 | remoteGlobalIDString = 47E429C51DCCE2FD002BE498;
34 | remoteInfo = TestHostApp;
35 | };
36 | 9694C2BC1A66097F005B3030 /* PBXContainerItemProxy */ = {
37 | isa = PBXContainerItemProxy;
38 | containerPortal = 961101BC19D2581700E6A6E3 /* Project object */;
39 | proxyType = 1;
40 | remoteGlobalIDString = 9694C2AF1A66097F005B3030;
41 | remoteInfo = SwiftKeychainWrapper;
42 | };
43 | /* End PBXContainerItemProxy section */
44 |
45 | /* Begin PBXFileReference section */
46 | 1AC6BBD91CCD272C00DE429B /* KeychainItemAccessibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainItemAccessibility.swift; sourceTree = ""; };
47 | 470A7D3A1CCE40EE001768EE /* KeychainWrapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainWrapperTests.swift; sourceTree = ""; };
48 | 477728B81CAF29E500905FE4 /* KeychainWrapperPrimitiveValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainWrapperPrimitiveValueTests.swift; sourceTree = ""; };
49 | 47E429C61DCCE2FD002BE498 /* TestHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
50 | 47E429C81DCCE2FD002BE498 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
51 | 47E429CA1DCCE2FD002BE498 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
52 | 47E429CD1DCCE2FD002BE498 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
53 | 47E429CF1DCCE2FD002BE498 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 47E429D21DCCE2FD002BE498 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
55 | 47E429D41DCCE2FD002BE498 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 47E429D81DCCE31C002BE498 /* Debug.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Debug.entitlements; sourceTree = ""; };
57 | 5E79BF2E246847600035CAC0 /* KeychainWrapperSubscript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainWrapperSubscript.swift; sourceTree = ""; };
58 | 961101D419D26F2800E6A6E3 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
59 | 96405CF31CA57F460080FF45 /* KeychainWrapperDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainWrapperDeleteTests.swift; sourceTree = ""; };
60 | 9661773F1D597872000772C5 /* KeychainWrapperDefaultWrapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainWrapperDefaultWrapperTests.swift; sourceTree = ""; };
61 | 966E4AAF1B96693F00A8DCEB /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
62 | 9694C2B01A66097F005B3030 /* SwiftKeychainWrapper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftKeychainWrapper.framework; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 9694C2B31A66097F005B3030 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
64 | 9694C2B41A66097F005B3030 /* SwiftKeychainWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftKeychainWrapper.h; sourceTree = ""; };
65 | 9694C2BA1A66097F005B3030 /* SwiftKeychainWrapperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftKeychainWrapperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
66 | 9694C2C01A66097F005B3030 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
67 | 9694C2CC1A660F90005B3030 /* KeychainWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainWrapper.swift; sourceTree = ""; };
68 | 9694C2CF1A660F9C005B3030 /* TestObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestObject.swift; sourceTree = ""; };
69 | /* End PBXFileReference section */
70 |
71 | /* Begin PBXFrameworksBuildPhase section */
72 | 47E429C31DCCE2FD002BE498 /* Frameworks */ = {
73 | isa = PBXFrameworksBuildPhase;
74 | buildActionMask = 2147483647;
75 | files = (
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | 9694C2AC1A66097F005B3030 /* Frameworks */ = {
80 | isa = PBXFrameworksBuildPhase;
81 | buildActionMask = 2147483647;
82 | files = (
83 | );
84 | runOnlyForDeploymentPostprocessing = 0;
85 | };
86 | 9694C2B71A66097F005B3030 /* Frameworks */ = {
87 | isa = PBXFrameworksBuildPhase;
88 | buildActionMask = 2147483647;
89 | files = (
90 | 9694C2BB1A66097F005B3030 /* SwiftKeychainWrapper.framework in Frameworks */,
91 | );
92 | runOnlyForDeploymentPostprocessing = 0;
93 | };
94 | /* End PBXFrameworksBuildPhase section */
95 |
96 | /* Begin PBXGroup section */
97 | 47E429C71DCCE2FD002BE498 /* TestHostApp */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 47E429C81DCCE2FD002BE498 /* AppDelegate.swift */,
101 | 47E429CA1DCCE2FD002BE498 /* ViewController.swift */,
102 | 47E429CC1DCCE2FD002BE498 /* Main.storyboard */,
103 | 47E429CF1DCCE2FD002BE498 /* Assets.xcassets */,
104 | 47E429D11DCCE2FD002BE498 /* LaunchScreen.storyboard */,
105 | 47E429D41DCCE2FD002BE498 /* Info.plist */,
106 | 47E429D81DCCE31C002BE498 /* Debug.entitlements */,
107 | );
108 | path = TestHostApp;
109 | sourceTree = "";
110 | };
111 | 961101BB19D2581700E6A6E3 = {
112 | isa = PBXGroup;
113 | children = (
114 | 966E4AAF1B96693F00A8DCEB /* LICENSE */,
115 | 961101D419D26F2800E6A6E3 /* README.md */,
116 | 9694C2B11A66097F005B3030 /* SwiftKeychainWrapper */,
117 | 9694C2BE1A66097F005B3030 /* SwiftKeychainWrapperTests */,
118 | 47E429C71DCCE2FD002BE498 /* TestHostApp */,
119 | 961101C719D2583500E6A6E3 /* Products */,
120 | );
121 | sourceTree = "";
122 | };
123 | 961101C719D2583500E6A6E3 /* Products */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 9694C2B01A66097F005B3030 /* SwiftKeychainWrapper.framework */,
127 | 9694C2BA1A66097F005B3030 /* SwiftKeychainWrapperTests.xctest */,
128 | 47E429C61DCCE2FD002BE498 /* TestHostApp.app */,
129 | );
130 | name = Products;
131 | sourceTree = "";
132 | };
133 | 9694C2B11A66097F005B3030 /* SwiftKeychainWrapper */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 9694C2B41A66097F005B3030 /* SwiftKeychainWrapper.h */,
137 | 5E79BF2E246847600035CAC0 /* KeychainWrapperSubscript.swift */,
138 | 9694C2CC1A660F90005B3030 /* KeychainWrapper.swift */,
139 | 1AC6BBD91CCD272C00DE429B /* KeychainItemAccessibility.swift */,
140 | 9694C2B21A66097F005B3030 /* Supporting Files */,
141 | );
142 | path = SwiftKeychainWrapper;
143 | sourceTree = "";
144 | };
145 | 9694C2B21A66097F005B3030 /* Supporting Files */ = {
146 | isa = PBXGroup;
147 | children = (
148 | 9694C2B31A66097F005B3030 /* Info.plist */,
149 | );
150 | name = "Supporting Files";
151 | sourceTree = "";
152 | };
153 | 9694C2BE1A66097F005B3030 /* SwiftKeychainWrapperTests */ = {
154 | isa = PBXGroup;
155 | children = (
156 | 9694C2CF1A660F9C005B3030 /* TestObject.swift */,
157 | 470A7D3A1CCE40EE001768EE /* KeychainWrapperTests.swift */,
158 | 9661773F1D597872000772C5 /* KeychainWrapperDefaultWrapperTests.swift */,
159 | 96405CF31CA57F460080FF45 /* KeychainWrapperDeleteTests.swift */,
160 | 477728B81CAF29E500905FE4 /* KeychainWrapperPrimitiveValueTests.swift */,
161 | 9694C2BF1A66097F005B3030 /* Supporting Files */,
162 | );
163 | path = SwiftKeychainWrapperTests;
164 | sourceTree = "";
165 | };
166 | 9694C2BF1A66097F005B3030 /* Supporting Files */ = {
167 | isa = PBXGroup;
168 | children = (
169 | 9694C2C01A66097F005B3030 /* Info.plist */,
170 | );
171 | name = "Supporting Files";
172 | sourceTree = "";
173 | };
174 | /* End PBXGroup section */
175 |
176 | /* Begin PBXHeadersBuildPhase section */
177 | 9694C2AD1A66097F005B3030 /* Headers */ = {
178 | isa = PBXHeadersBuildPhase;
179 | buildActionMask = 2147483647;
180 | files = (
181 | 9694C2B51A66097F005B3030 /* SwiftKeychainWrapper.h in Headers */,
182 | );
183 | runOnlyForDeploymentPostprocessing = 0;
184 | };
185 | /* End PBXHeadersBuildPhase section */
186 |
187 | /* Begin PBXNativeTarget section */
188 | 47E429C51DCCE2FD002BE498 /* TestHostApp */ = {
189 | isa = PBXNativeTarget;
190 | buildConfigurationList = 47E429D71DCCE2FD002BE498 /* Build configuration list for PBXNativeTarget "TestHostApp" */;
191 | buildPhases = (
192 | 47E429C21DCCE2FD002BE498 /* Sources */,
193 | 47E429C31DCCE2FD002BE498 /* Frameworks */,
194 | 47E429C41DCCE2FD002BE498 /* Resources */,
195 | );
196 | buildRules = (
197 | );
198 | dependencies = (
199 | );
200 | name = TestHostApp;
201 | productName = TestHostApp;
202 | productReference = 47E429C61DCCE2FD002BE498 /* TestHostApp.app */;
203 | productType = "com.apple.product-type.application";
204 | };
205 | 9694C2AF1A66097F005B3030 /* SwiftKeychainWrapper */ = {
206 | isa = PBXNativeTarget;
207 | buildConfigurationList = 9694C2C71A66097F005B3030 /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapper" */;
208 | buildPhases = (
209 | 9694C2AB1A66097F005B3030 /* Sources */,
210 | 9694C2AC1A66097F005B3030 /* Frameworks */,
211 | 9694C2AD1A66097F005B3030 /* Headers */,
212 | 9694C2AE1A66097F005B3030 /* Resources */,
213 | );
214 | buildRules = (
215 | );
216 | dependencies = (
217 | );
218 | name = SwiftKeychainWrapper;
219 | productName = SwiftKeychainWrapper;
220 | productReference = 9694C2B01A66097F005B3030 /* SwiftKeychainWrapper.framework */;
221 | productType = "com.apple.product-type.framework";
222 | };
223 | 9694C2B91A66097F005B3030 /* SwiftKeychainWrapperTests */ = {
224 | isa = PBXNativeTarget;
225 | buildConfigurationList = 9694C2C81A66097F005B3030 /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapperTests" */;
226 | buildPhases = (
227 | 9694C2B61A66097F005B3030 /* Sources */,
228 | 9694C2B71A66097F005B3030 /* Frameworks */,
229 | 9694C2B81A66097F005B3030 /* Resources */,
230 | );
231 | buildRules = (
232 | );
233 | dependencies = (
234 | 9694C2BD1A66097F005B3030 /* PBXTargetDependency */,
235 | 47E429DA1DCCE408002BE498 /* PBXTargetDependency */,
236 | );
237 | name = SwiftKeychainWrapperTests;
238 | productName = SwiftKeychainWrapperTests;
239 | productReference = 9694C2BA1A66097F005B3030 /* SwiftKeychainWrapperTests.xctest */;
240 | productType = "com.apple.product-type.bundle.unit-test";
241 | };
242 | /* End PBXNativeTarget section */
243 |
244 | /* Begin PBXProject section */
245 | 961101BC19D2581700E6A6E3 /* Project object */ = {
246 | isa = PBXProject;
247 | attributes = {
248 | LastSwiftMigration = 0700;
249 | LastSwiftUpdateCheck = 0810;
250 | LastUpgradeCheck = 1200;
251 | ORGANIZATIONNAME = "Jason Rendel";
252 | TargetAttributes = {
253 | 47E429C51DCCE2FD002BE498 = {
254 | CreatedOnToolsVersion = 8.1;
255 | LastSwiftMigration = 1020;
256 | ProvisioningStyle = Automatic;
257 | };
258 | 9694C2AF1A66097F005B3030 = {
259 | CreatedOnToolsVersion = 6.1.1;
260 | LastSwiftMigration = 1020;
261 | };
262 | 9694C2B91A66097F005B3030 = {
263 | CreatedOnToolsVersion = 6.1.1;
264 | DevelopmentTeam = QDW4H7J3NE;
265 | LastSwiftMigration = 1020;
266 | TestTargetID = 47E429C51DCCE2FD002BE498;
267 | };
268 | };
269 | };
270 | buildConfigurationList = 961101BF19D2581700E6A6E3 /* Build configuration list for PBXProject "SwiftKeychainWrapper" */;
271 | compatibilityVersion = "Xcode 3.2";
272 | developmentRegion = en;
273 | hasScannedForEncodings = 0;
274 | knownRegions = (
275 | en,
276 | Base,
277 | );
278 | mainGroup = 961101BB19D2581700E6A6E3;
279 | productRefGroup = 961101C719D2583500E6A6E3 /* Products */;
280 | projectDirPath = "";
281 | projectRoot = "";
282 | targets = (
283 | 9694C2AF1A66097F005B3030 /* SwiftKeychainWrapper */,
284 | 9694C2B91A66097F005B3030 /* SwiftKeychainWrapperTests */,
285 | 47E429C51DCCE2FD002BE498 /* TestHostApp */,
286 | );
287 | };
288 | /* End PBXProject section */
289 |
290 | /* Begin PBXResourcesBuildPhase section */
291 | 47E429C41DCCE2FD002BE498 /* Resources */ = {
292 | isa = PBXResourcesBuildPhase;
293 | buildActionMask = 2147483647;
294 | files = (
295 | 47E429D31DCCE2FD002BE498 /* LaunchScreen.storyboard in Resources */,
296 | 47E429D01DCCE2FD002BE498 /* Assets.xcassets in Resources */,
297 | 47E429CE1DCCE2FD002BE498 /* Main.storyboard in Resources */,
298 | );
299 | runOnlyForDeploymentPostprocessing = 0;
300 | };
301 | 9694C2AE1A66097F005B3030 /* Resources */ = {
302 | isa = PBXResourcesBuildPhase;
303 | buildActionMask = 2147483647;
304 | files = (
305 | 966E4AB01B96693F00A8DCEB /* LICENSE in Resources */,
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | };
309 | 9694C2B81A66097F005B3030 /* Resources */ = {
310 | isa = PBXResourcesBuildPhase;
311 | buildActionMask = 2147483647;
312 | files = (
313 | );
314 | runOnlyForDeploymentPostprocessing = 0;
315 | };
316 | /* End PBXResourcesBuildPhase section */
317 |
318 | /* Begin PBXSourcesBuildPhase section */
319 | 47E429C21DCCE2FD002BE498 /* Sources */ = {
320 | isa = PBXSourcesBuildPhase;
321 | buildActionMask = 2147483647;
322 | files = (
323 | 47E429CB1DCCE2FD002BE498 /* ViewController.swift in Sources */,
324 | 47E429C91DCCE2FD002BE498 /* AppDelegate.swift in Sources */,
325 | );
326 | runOnlyForDeploymentPostprocessing = 0;
327 | };
328 | 9694C2AB1A66097F005B3030 /* Sources */ = {
329 | isa = PBXSourcesBuildPhase;
330 | buildActionMask = 2147483647;
331 | files = (
332 | 5E79BF2F246847600035CAC0 /* KeychainWrapperSubscript.swift in Sources */,
333 | 9694C2CD1A660F90005B3030 /* KeychainWrapper.swift in Sources */,
334 | 1AC6BBDA1CCD272C00DE429B /* KeychainItemAccessibility.swift in Sources */,
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | 9694C2B61A66097F005B3030 /* Sources */ = {
339 | isa = PBXSourcesBuildPhase;
340 | buildActionMask = 2147483647;
341 | files = (
342 | 9694C2D11A660F9C005B3030 /* TestObject.swift in Sources */,
343 | 96405CF41CA57F460080FF45 /* KeychainWrapperDeleteTests.swift in Sources */,
344 | 477728B91CAF29E500905FE4 /* KeychainWrapperPrimitiveValueTests.swift in Sources */,
345 | 966177401D597872000772C5 /* KeychainWrapperDefaultWrapperTests.swift in Sources */,
346 | 470A7D3B1CCE40EE001768EE /* KeychainWrapperTests.swift in Sources */,
347 | );
348 | runOnlyForDeploymentPostprocessing = 0;
349 | };
350 | /* End PBXSourcesBuildPhase section */
351 |
352 | /* Begin PBXTargetDependency section */
353 | 47E429DA1DCCE408002BE498 /* PBXTargetDependency */ = {
354 | isa = PBXTargetDependency;
355 | target = 47E429C51DCCE2FD002BE498 /* TestHostApp */;
356 | targetProxy = 47E429D91DCCE408002BE498 /* PBXContainerItemProxy */;
357 | };
358 | 9694C2BD1A66097F005B3030 /* PBXTargetDependency */ = {
359 | isa = PBXTargetDependency;
360 | target = 9694C2AF1A66097F005B3030 /* SwiftKeychainWrapper */;
361 | targetProxy = 9694C2BC1A66097F005B3030 /* PBXContainerItemProxy */;
362 | };
363 | /* End PBXTargetDependency section */
364 |
365 | /* Begin PBXVariantGroup section */
366 | 47E429CC1DCCE2FD002BE498 /* Main.storyboard */ = {
367 | isa = PBXVariantGroup;
368 | children = (
369 | 47E429CD1DCCE2FD002BE498 /* Base */,
370 | );
371 | name = Main.storyboard;
372 | sourceTree = "";
373 | };
374 | 47E429D11DCCE2FD002BE498 /* LaunchScreen.storyboard */ = {
375 | isa = PBXVariantGroup;
376 | children = (
377 | 47E429D21DCCE2FD002BE498 /* Base */,
378 | );
379 | name = LaunchScreen.storyboard;
380 | sourceTree = "";
381 | };
382 | /* End PBXVariantGroup section */
383 |
384 | /* Begin XCBuildConfiguration section */
385 | 47E429D51DCCE2FD002BE498 /* Debug */ = {
386 | isa = XCBuildConfiguration;
387 | buildSettings = {
388 | ALWAYS_SEARCH_USER_PATHS = NO;
389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
390 | CLANG_ANALYZER_NONNULL = YES;
391 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
392 | CLANG_CXX_LIBRARY = "libc++";
393 | CLANG_ENABLE_MODULES = YES;
394 | CLANG_ENABLE_OBJC_ARC = YES;
395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
396 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
398 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
399 | CODE_SIGN_ENTITLEMENTS = Debug.entitlements;
400 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
401 | COPY_PHASE_STRIP = NO;
402 | DEBUG_INFORMATION_FORMAT = dwarf;
403 | DEVELOPMENT_TEAM = "";
404 | GCC_C_LANGUAGE_STANDARD = gnu99;
405 | GCC_DYNAMIC_NO_PIC = NO;
406 | GCC_OPTIMIZATION_LEVEL = 0;
407 | GCC_PREPROCESSOR_DEFINITIONS = (
408 | "DEBUG=1",
409 | "$(inherited)",
410 | );
411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
413 | INFOPLIST_FILE = TestHostApp/Info.plist;
414 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
415 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
416 | MTL_ENABLE_DEBUG_INFO = YES;
417 | PRODUCT_BUNDLE_IDENTIFIER = com.jasonrendel.TestHostApp;
418 | PRODUCT_NAME = "$(TARGET_NAME)";
419 | SDKROOT = iphoneos;
420 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
421 | TARGETED_DEVICE_FAMILY = "1,2";
422 | };
423 | name = Debug;
424 | };
425 | 47E429D61DCCE2FD002BE498 /* Release */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
430 | CLANG_ANALYZER_NONNULL = YES;
431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
432 | CLANG_CXX_LIBRARY = "libc++";
433 | CLANG_ENABLE_MODULES = YES;
434 | CLANG_ENABLE_OBJC_ARC = YES;
435 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
436 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
438 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
439 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
440 | COPY_PHASE_STRIP = NO;
441 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
442 | DEVELOPMENT_TEAM = "";
443 | ENABLE_NS_ASSERTIONS = NO;
444 | GCC_C_LANGUAGE_STANDARD = gnu99;
445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
446 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
447 | INFOPLIST_FILE = TestHostApp/Info.plist;
448 | IPHONEOS_DEPLOYMENT_TARGET = 10.1;
449 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
450 | MTL_ENABLE_DEBUG_INFO = NO;
451 | PRODUCT_BUNDLE_IDENTIFIER = com.jasonrendel.TestHostApp;
452 | PRODUCT_NAME = "$(TARGET_NAME)";
453 | SDKROOT = iphoneos;
454 | TARGETED_DEVICE_FAMILY = "1,2";
455 | VALIDATE_PRODUCT = YES;
456 | };
457 | name = Release;
458 | };
459 | 961101C019D2581700E6A6E3 /* Debug */ = {
460 | isa = XCBuildConfiguration;
461 | buildSettings = {
462 | APPLICATION_EXTENSION_API_ONLY = YES;
463 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
464 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
465 | CLANG_WARN_BOOL_CONVERSION = YES;
466 | CLANG_WARN_COMMA = YES;
467 | CLANG_WARN_CONSTANT_CONVERSION = YES;
468 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
469 | CLANG_WARN_EMPTY_BODY = YES;
470 | CLANG_WARN_ENUM_CONVERSION = YES;
471 | CLANG_WARN_INFINITE_RECURSION = YES;
472 | CLANG_WARN_INT_CONVERSION = YES;
473 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
474 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
475 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
476 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
477 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
478 | CLANG_WARN_STRICT_PROTOTYPES = YES;
479 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
480 | CLANG_WARN_UNREACHABLE_CODE = YES;
481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
482 | ENABLE_STRICT_OBJC_MSGSEND = YES;
483 | ENABLE_TESTABILITY = YES;
484 | GCC_NO_COMMON_BLOCKS = YES;
485 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
486 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
487 | GCC_WARN_UNDECLARED_SELECTOR = YES;
488 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
489 | GCC_WARN_UNUSED_FUNCTION = YES;
490 | GCC_WARN_UNUSED_VARIABLE = YES;
491 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
492 | ONLY_ACTIVE_ARCH = YES;
493 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
494 | SWIFT_VERSION = 5.0;
495 | };
496 | name = Debug;
497 | };
498 | 961101C119D2581700E6A6E3 /* Release */ = {
499 | isa = XCBuildConfiguration;
500 | buildSettings = {
501 | APPLICATION_EXTENSION_API_ONLY = YES;
502 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
503 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
504 | CLANG_WARN_BOOL_CONVERSION = YES;
505 | CLANG_WARN_COMMA = YES;
506 | CLANG_WARN_CONSTANT_CONVERSION = YES;
507 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
508 | CLANG_WARN_EMPTY_BODY = YES;
509 | CLANG_WARN_ENUM_CONVERSION = YES;
510 | CLANG_WARN_INFINITE_RECURSION = YES;
511 | CLANG_WARN_INT_CONVERSION = YES;
512 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
513 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
514 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
515 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
516 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
517 | CLANG_WARN_STRICT_PROTOTYPES = YES;
518 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
519 | CLANG_WARN_UNREACHABLE_CODE = YES;
520 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
521 | ENABLE_STRICT_OBJC_MSGSEND = YES;
522 | GCC_NO_COMMON_BLOCKS = YES;
523 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
524 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
525 | GCC_WARN_UNDECLARED_SELECTOR = YES;
526 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
527 | GCC_WARN_UNUSED_FUNCTION = YES;
528 | GCC_WARN_UNUSED_VARIABLE = YES;
529 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
530 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
531 | SWIFT_VERSION = 5.0;
532 | };
533 | name = Release;
534 | };
535 | 9694C2C31A66097F005B3030 /* Debug */ = {
536 | isa = XCBuildConfiguration;
537 | buildSettings = {
538 | ALWAYS_SEARCH_USER_PATHS = NO;
539 | APPLICATION_EXTENSION_API_ONLY = YES;
540 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
541 | CLANG_CXX_LIBRARY = "libc++";
542 | CLANG_ENABLE_MODULES = YES;
543 | CLANG_ENABLE_OBJC_ARC = YES;
544 | CLANG_WARN_BOOL_CONVERSION = YES;
545 | CLANG_WARN_CONSTANT_CONVERSION = YES;
546 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
547 | CLANG_WARN_EMPTY_BODY = YES;
548 | CLANG_WARN_ENUM_CONVERSION = YES;
549 | CLANG_WARN_INT_CONVERSION = YES;
550 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
551 | CLANG_WARN_UNREACHABLE_CODE = YES;
552 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
553 | CODE_SIGN_IDENTITY = "";
554 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
555 | COPY_PHASE_STRIP = NO;
556 | CURRENT_PROJECT_VERSION = 1;
557 | DEFINES_MODULE = YES;
558 | DYLIB_COMPATIBILITY_VERSION = 1;
559 | DYLIB_CURRENT_VERSION = 1;
560 | DYLIB_INSTALL_NAME_BASE = "@rpath";
561 | ENABLE_STRICT_OBJC_MSGSEND = YES;
562 | GCC_C_LANGUAGE_STANDARD = gnu99;
563 | GCC_DYNAMIC_NO_PIC = NO;
564 | GCC_OPTIMIZATION_LEVEL = 0;
565 | GCC_PREPROCESSOR_DEFINITIONS = (
566 | "DEBUG=1",
567 | "$(inherited)",
568 | );
569 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
570 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
571 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
572 | GCC_WARN_UNDECLARED_SELECTOR = YES;
573 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
574 | GCC_WARN_UNUSED_FUNCTION = YES;
575 | GCC_WARN_UNUSED_VARIABLE = YES;
576 | INFOPLIST_FILE = SwiftKeychainWrapper/Info.plist;
577 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
579 | MTL_ENABLE_DEBUG_INFO = YES;
580 | PRODUCT_BUNDLE_IDENTIFIER = "com.jasonrendel.$(PRODUCT_NAME:rfc1034identifier)";
581 | PRODUCT_NAME = "$(TARGET_NAME)";
582 | SDKROOT = iphoneos;
583 | SKIP_INSTALL = YES;
584 | TARGETED_DEVICE_FAMILY = "1,2";
585 | VERSIONING_SYSTEM = "apple-generic";
586 | VERSION_INFO_PREFIX = "";
587 | };
588 | name = Debug;
589 | };
590 | 9694C2C41A66097F005B3030 /* Release */ = {
591 | isa = XCBuildConfiguration;
592 | buildSettings = {
593 | ALWAYS_SEARCH_USER_PATHS = NO;
594 | APPLICATION_EXTENSION_API_ONLY = YES;
595 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
596 | CLANG_CXX_LIBRARY = "libc++";
597 | CLANG_ENABLE_MODULES = YES;
598 | CLANG_ENABLE_OBJC_ARC = YES;
599 | CLANG_WARN_BOOL_CONVERSION = YES;
600 | CLANG_WARN_CONSTANT_CONVERSION = YES;
601 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
602 | CLANG_WARN_EMPTY_BODY = YES;
603 | CLANG_WARN_ENUM_CONVERSION = YES;
604 | CLANG_WARN_INT_CONVERSION = YES;
605 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
606 | CLANG_WARN_UNREACHABLE_CODE = YES;
607 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
608 | CODE_SIGN_IDENTITY = "";
609 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
610 | COPY_PHASE_STRIP = YES;
611 | CURRENT_PROJECT_VERSION = 1;
612 | DEFINES_MODULE = YES;
613 | DYLIB_COMPATIBILITY_VERSION = 1;
614 | DYLIB_CURRENT_VERSION = 1;
615 | DYLIB_INSTALL_NAME_BASE = "@rpath";
616 | ENABLE_NS_ASSERTIONS = NO;
617 | ENABLE_STRICT_OBJC_MSGSEND = YES;
618 | GCC_C_LANGUAGE_STANDARD = gnu99;
619 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
620 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
621 | GCC_WARN_UNDECLARED_SELECTOR = YES;
622 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
623 | GCC_WARN_UNUSED_FUNCTION = YES;
624 | GCC_WARN_UNUSED_VARIABLE = YES;
625 | INFOPLIST_FILE = SwiftKeychainWrapper/Info.plist;
626 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
627 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
628 | MTL_ENABLE_DEBUG_INFO = NO;
629 | PRODUCT_BUNDLE_IDENTIFIER = "com.jasonrendel.$(PRODUCT_NAME:rfc1034identifier)";
630 | PRODUCT_NAME = "$(TARGET_NAME)";
631 | SDKROOT = iphoneos;
632 | SKIP_INSTALL = YES;
633 | TARGETED_DEVICE_FAMILY = "1,2";
634 | VALIDATE_PRODUCT = YES;
635 | VERSIONING_SYSTEM = "apple-generic";
636 | VERSION_INFO_PREFIX = "";
637 | };
638 | name = Release;
639 | };
640 | 9694C2C51A66097F005B3030 /* Debug */ = {
641 | isa = XCBuildConfiguration;
642 | buildSettings = {
643 | ALWAYS_SEARCH_USER_PATHS = NO;
644 | APPLICATION_EXTENSION_API_ONLY = NO;
645 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
646 | CLANG_CXX_LIBRARY = "libc++";
647 | CLANG_ENABLE_MODULES = YES;
648 | CLANG_ENABLE_OBJC_ARC = YES;
649 | CLANG_WARN_BOOL_CONVERSION = YES;
650 | CLANG_WARN_CONSTANT_CONVERSION = YES;
651 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
652 | CLANG_WARN_EMPTY_BODY = YES;
653 | CLANG_WARN_ENUM_CONVERSION = YES;
654 | CLANG_WARN_INT_CONVERSION = YES;
655 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
656 | CLANG_WARN_UNREACHABLE_CODE = YES;
657 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
658 | CODE_SIGN_IDENTITY = "iPhone Developer";
659 | COPY_PHASE_STRIP = NO;
660 | DEVELOPMENT_TEAM = QDW4H7J3NE;
661 | ENABLE_STRICT_OBJC_MSGSEND = YES;
662 | GCC_C_LANGUAGE_STANDARD = gnu99;
663 | GCC_DYNAMIC_NO_PIC = NO;
664 | GCC_OPTIMIZATION_LEVEL = 0;
665 | GCC_PREPROCESSOR_DEFINITIONS = (
666 | "DEBUG=1",
667 | "$(inherited)",
668 | );
669 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
670 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
671 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
672 | GCC_WARN_UNDECLARED_SELECTOR = YES;
673 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
674 | GCC_WARN_UNUSED_FUNCTION = YES;
675 | GCC_WARN_UNUSED_VARIABLE = YES;
676 | INFOPLIST_FILE = SwiftKeychainWrapperTests/Info.plist;
677 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
678 | MTL_ENABLE_DEBUG_INFO = YES;
679 | PRODUCT_BUNDLE_IDENTIFIER = "com.jasonrendel.$(PRODUCT_NAME:rfc1034identifier)";
680 | PRODUCT_NAME = "$(TARGET_NAME)";
681 | SDKROOT = iphoneos;
682 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHostApp.app/TestHostApp";
683 | };
684 | name = Debug;
685 | };
686 | 9694C2C61A66097F005B3030 /* Release */ = {
687 | isa = XCBuildConfiguration;
688 | buildSettings = {
689 | ALWAYS_SEARCH_USER_PATHS = NO;
690 | APPLICATION_EXTENSION_API_ONLY = NO;
691 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
692 | CLANG_CXX_LIBRARY = "libc++";
693 | CLANG_ENABLE_MODULES = YES;
694 | CLANG_ENABLE_OBJC_ARC = YES;
695 | CLANG_WARN_BOOL_CONVERSION = YES;
696 | CLANG_WARN_CONSTANT_CONVERSION = YES;
697 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
698 | CLANG_WARN_EMPTY_BODY = YES;
699 | CLANG_WARN_ENUM_CONVERSION = YES;
700 | CLANG_WARN_INT_CONVERSION = YES;
701 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
702 | CLANG_WARN_UNREACHABLE_CODE = YES;
703 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
704 | CODE_SIGN_IDENTITY = "iPhone Developer";
705 | COPY_PHASE_STRIP = YES;
706 | DEVELOPMENT_TEAM = QDW4H7J3NE;
707 | ENABLE_NS_ASSERTIONS = NO;
708 | ENABLE_STRICT_OBJC_MSGSEND = YES;
709 | GCC_C_LANGUAGE_STANDARD = gnu99;
710 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
711 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
712 | GCC_WARN_UNDECLARED_SELECTOR = YES;
713 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
714 | GCC_WARN_UNUSED_FUNCTION = YES;
715 | GCC_WARN_UNUSED_VARIABLE = YES;
716 | INFOPLIST_FILE = SwiftKeychainWrapperTests/Info.plist;
717 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
718 | MTL_ENABLE_DEBUG_INFO = NO;
719 | PRODUCT_BUNDLE_IDENTIFIER = "com.jasonrendel.$(PRODUCT_NAME:rfc1034identifier)";
720 | PRODUCT_NAME = "$(TARGET_NAME)";
721 | SDKROOT = iphoneos;
722 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHostApp.app/TestHostApp";
723 | VALIDATE_PRODUCT = YES;
724 | };
725 | name = Release;
726 | };
727 | /* End XCBuildConfiguration section */
728 |
729 | /* Begin XCConfigurationList section */
730 | 47E429D71DCCE2FD002BE498 /* Build configuration list for PBXNativeTarget "TestHostApp" */ = {
731 | isa = XCConfigurationList;
732 | buildConfigurations = (
733 | 47E429D51DCCE2FD002BE498 /* Debug */,
734 | 47E429D61DCCE2FD002BE498 /* Release */,
735 | );
736 | defaultConfigurationIsVisible = 0;
737 | defaultConfigurationName = Release;
738 | };
739 | 961101BF19D2581700E6A6E3 /* Build configuration list for PBXProject "SwiftKeychainWrapper" */ = {
740 | isa = XCConfigurationList;
741 | buildConfigurations = (
742 | 961101C019D2581700E6A6E3 /* Debug */,
743 | 961101C119D2581700E6A6E3 /* Release */,
744 | );
745 | defaultConfigurationIsVisible = 0;
746 | defaultConfigurationName = Release;
747 | };
748 | 9694C2C71A66097F005B3030 /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapper" */ = {
749 | isa = XCConfigurationList;
750 | buildConfigurations = (
751 | 9694C2C31A66097F005B3030 /* Debug */,
752 | 9694C2C41A66097F005B3030 /* Release */,
753 | );
754 | defaultConfigurationIsVisible = 0;
755 | defaultConfigurationName = Release;
756 | };
757 | 9694C2C81A66097F005B3030 /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapperTests" */ = {
758 | isa = XCConfigurationList;
759 | buildConfigurations = (
760 | 9694C2C51A66097F005B3030 /* Debug */,
761 | 9694C2C61A66097F005B3030 /* Release */,
762 | );
763 | defaultConfigurationIsVisible = 0;
764 | defaultConfigurationName = Release;
765 | };
766 | /* End XCConfigurationList section */
767 | };
768 | rootObject = 961101BC19D2581700E6A6E3 /* Project object */;
769 | }
770 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
51 |
52 |
53 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
76 |
77 |
83 |
84 |
85 |
86 |
92 |
93 |
99 |
100 |
101 |
102 |
104 |
105 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 4.0.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 4.0.1
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/KeychainItemAccessibility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainOptions.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by James Blair on 4/24/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 | protocol KeychainAttrRepresentable {
31 | var keychainAttrValue: CFString { get }
32 | }
33 |
34 | // MARK: - KeychainItemAccessibility
35 | public enum KeychainItemAccessibility {
36 | /**
37 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
38 |
39 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
40 | */
41 | @available(iOS 4, *)
42 | case afterFirstUnlock
43 |
44 | /**
45 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
46 |
47 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
48 | */
49 | @available(iOS 4, *)
50 | case afterFirstUnlockThisDeviceOnly
51 |
52 | /**
53 | The data in the keychain item can always be accessed regardless of whether the device is locked.
54 |
55 | This is not recommended for application use. Items with this attribute migrate to a new device when using encrypted backups.
56 | */
57 | @available(iOS 4, *)
58 | case always
59 |
60 | /**
61 | The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device.
62 |
63 | This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted.
64 | */
65 | @available(iOS 8, *)
66 | case whenPasscodeSetThisDeviceOnly
67 |
68 | /**
69 | The data in the keychain item can always be accessed regardless of whether the device is locked.
70 |
71 | This is not recommended for application use. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
72 | */
73 | @available(iOS 4, *)
74 | case alwaysThisDeviceOnly
75 |
76 | /**
77 | The data in the keychain item can be accessed only while the device is unlocked by the user.
78 |
79 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups.
80 |
81 | This is the default value for keychain items added without explicitly setting an accessibility constant.
82 | */
83 | @available(iOS 4, *)
84 | case whenUnlocked
85 |
86 | /**
87 | The data in the keychain item can be accessed only while the device is unlocked by the user.
88 |
89 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
90 | */
91 | @available(iOS 4, *)
92 | case whenUnlockedThisDeviceOnly
93 |
94 | static func accessibilityForAttributeValue(_ keychainAttrValue: CFString) -> KeychainItemAccessibility? {
95 | for (key, value) in keychainItemAccessibilityLookup {
96 | if value == keychainAttrValue {
97 | return key
98 | }
99 | }
100 |
101 | return nil
102 | }
103 | }
104 |
105 | private let keychainItemAccessibilityLookup: [KeychainItemAccessibility:CFString] = {
106 | var lookup: [KeychainItemAccessibility:CFString] = [
107 | .afterFirstUnlock: kSecAttrAccessibleAfterFirstUnlock,
108 | .afterFirstUnlockThisDeviceOnly: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
109 | .always: kSecAttrAccessibleAlways,
110 | .whenPasscodeSetThisDeviceOnly: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
111 | .alwaysThisDeviceOnly : kSecAttrAccessibleAlwaysThisDeviceOnly,
112 | .whenUnlocked: kSecAttrAccessibleWhenUnlocked,
113 | .whenUnlockedThisDeviceOnly: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
114 | ]
115 |
116 | return lookup
117 | }()
118 |
119 | extension KeychainItemAccessibility : KeychainAttrRepresentable {
120 | internal var keychainAttrValue: CFString {
121 | return keychainItemAccessibilityLookup[self]!
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/KeychainWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapper.swift
3 | // KeychainWrapper
4 | //
5 | // Created by Jason Rendel on 9/23/14.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 |
31 | private let SecMatchLimit: String! = kSecMatchLimit as String
32 | private let SecReturnData: String! = kSecReturnData as String
33 | private let SecReturnPersistentRef: String! = kSecReturnPersistentRef as String
34 | private let SecValueData: String! = kSecValueData as String
35 | private let SecAttrAccessible: String! = kSecAttrAccessible as String
36 | private let SecClass: String! = kSecClass as String
37 | private let SecAttrService: String! = kSecAttrService as String
38 | private let SecAttrGeneric: String! = kSecAttrGeneric as String
39 | private let SecAttrAccount: String! = kSecAttrAccount as String
40 | private let SecAttrAccessGroup: String! = kSecAttrAccessGroup as String
41 | private let SecReturnAttributes: String = kSecReturnAttributes as String
42 | private let SecAttrSynchronizable: String = kSecAttrSynchronizable as String
43 |
44 | /// KeychainWrapper is a class to help make Keychain access in Swift more straightforward. It is designed to make accessing the Keychain services more like using NSUserDefaults, which is much more familiar to people.
45 | open class KeychainWrapper {
46 |
47 | @available(*, deprecated, message: "KeychainWrapper.defaultKeychainWrapper is deprecated since version 2.2.1, use KeychainWrapper.standard instead")
48 | public static let defaultKeychainWrapper = KeychainWrapper.standard
49 |
50 | /// Default keychain wrapper access
51 | public static let standard = KeychainWrapper()
52 |
53 | /// ServiceName is used for the kSecAttrService property to uniquely identify this keychain accessor. If no service name is specified, KeychainWrapper will default to using the bundleIdentifier.
54 | private (set) public var serviceName: String
55 |
56 | /// AccessGroup is used for the kSecAttrAccessGroup property to identify which Keychain Access Group this entry belongs to. This allows you to use the KeychainWrapper with shared keychain access between different applications.
57 | private (set) public var accessGroup: String?
58 |
59 | private static let defaultServiceName: String = {
60 | return Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper"
61 | }()
62 |
63 | private convenience init() {
64 | self.init(serviceName: KeychainWrapper.defaultServiceName)
65 | }
66 |
67 | /// Create a custom instance of KeychainWrapper with a custom Service Name and optional custom access group.
68 | ///
69 | /// - parameter serviceName: The ServiceName for this instance. Used to uniquely identify all keys stored using this keychain wrapper instance.
70 | /// - parameter accessGroup: Optional unique AccessGroup for this instance. Use a matching AccessGroup between applications to allow shared keychain access.
71 | public init(serviceName: String, accessGroup: String? = nil) {
72 | self.serviceName = serviceName
73 | self.accessGroup = accessGroup
74 | }
75 |
76 | // MARK:- Public Methods
77 |
78 | /// Checks if keychain data exists for a specified key.
79 | ///
80 | /// - parameter forKey: The key to check for.
81 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
82 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
83 | /// - returns: True if a value exists for the key. False otherwise.
84 | open func hasValue(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
85 | if let _ = data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) {
86 | return true
87 | } else {
88 | return false
89 | }
90 | }
91 |
92 | open func accessibilityOfKey(_ key: String) -> KeychainItemAccessibility? {
93 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key)
94 |
95 | // Remove accessibility attribute
96 | keychainQueryDictionary.removeValue(forKey: SecAttrAccessible)
97 |
98 | // Limit search results to one
99 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
100 |
101 | // Specify we want SecAttrAccessible returned
102 | keychainQueryDictionary[SecReturnAttributes] = kCFBooleanTrue
103 |
104 | // Search
105 | var result: AnyObject?
106 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
107 |
108 | guard status == noErr, let resultsDictionary = result as? [String:AnyObject], let accessibilityAttrValue = resultsDictionary[SecAttrAccessible] as? String else {
109 | return nil
110 | }
111 |
112 | return KeychainItemAccessibility.accessibilityForAttributeValue(accessibilityAttrValue as CFString)
113 | }
114 |
115 | /// Get the keys of all keychain entries matching the current ServiceName and AccessGroup if one is set.
116 | open func allKeys() -> Set {
117 | var keychainQueryDictionary: [String:Any] = [
118 | SecClass: kSecClassGenericPassword,
119 | SecAttrService: serviceName,
120 | SecReturnAttributes: kCFBooleanTrue!,
121 | SecMatchLimit: kSecMatchLimitAll,
122 | ]
123 |
124 | if let accessGroup = self.accessGroup {
125 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
126 | }
127 |
128 | var result: AnyObject?
129 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
130 |
131 | guard status == errSecSuccess else { return [] }
132 |
133 | var keys = Set()
134 | if let results = result as? [[AnyHashable: Any]] {
135 | for attributes in results {
136 | if let accountData = attributes[SecAttrAccount] as? Data,
137 | let key = String(data: accountData, encoding: String.Encoding.utf8) {
138 | keys.insert(key)
139 | } else if let accountData = attributes[kSecAttrAccount] as? Data,
140 | let key = String(data: accountData, encoding: String.Encoding.utf8) {
141 | keys.insert(key)
142 | }
143 | }
144 | }
145 | return keys
146 | }
147 |
148 | // MARK: Public Getters
149 |
150 | open func integer(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Int? {
151 | guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else {
152 | return nil
153 | }
154 |
155 | return numberValue.intValue
156 | }
157 |
158 | open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Float? {
159 | guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else {
160 | return nil
161 | }
162 |
163 | return numberValue.floatValue
164 | }
165 |
166 | open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Double? {
167 | guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else {
168 | return nil
169 | }
170 |
171 | return numberValue.doubleValue
172 | }
173 |
174 | open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool? {
175 | guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else {
176 | return nil
177 | }
178 |
179 | return numberValue.boolValue
180 | }
181 |
182 | /// Returns a string value for a specified key.
183 | ///
184 | /// - parameter forKey: The key to lookup data for.
185 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
186 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
187 | /// - returns: The String associated with the key if it exists. If no data exists, or the data found cannot be encoded as a string, returns nil.
188 | open func string(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> String? {
189 | guard let keychainData = data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else {
190 | return nil
191 | }
192 |
193 | return String(data: keychainData, encoding: String.Encoding.utf8) as String?
194 | }
195 |
196 | /// Returns an object that conforms to NSCoding for a specified key.
197 | ///
198 | /// - parameter forKey: The key to lookup data for.
199 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
200 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
201 | /// - returns: The decoded object associated with the key if it exists. If no data exists, or the data found cannot be decoded, returns nil.
202 | open func object(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> NSCoding? {
203 | guard let keychainData = data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else {
204 | return nil
205 | }
206 |
207 | return NSKeyedUnarchiver.unarchiveObject(with: keychainData) as? NSCoding
208 | }
209 |
210 |
211 | /// Returns a Data object for a specified key.
212 | ///
213 | /// - parameter forKey: The key to lookup data for.
214 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
215 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
216 | /// - returns: The Data object associated with the key if it exists. If no data exists, returns nil.
217 | open func data(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Data? {
218 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
219 |
220 | // Limit search results to one
221 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
222 |
223 | // Specify we want Data/CFData returned
224 | keychainQueryDictionary[SecReturnData] = kCFBooleanTrue
225 |
226 | // Search
227 | var result: AnyObject?
228 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
229 |
230 | return status == noErr ? result as? Data : nil
231 | }
232 |
233 |
234 | /// Returns a persistent data reference object for a specified key.
235 | ///
236 | /// - parameter forKey: The key to lookup data for.
237 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
238 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
239 | /// - returns: The persistent data reference object associated with the key if it exists. If no data exists, returns nil.
240 | open func dataRef(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Data? {
241 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
242 |
243 | // Limit search results to one
244 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
245 |
246 | // Specify we want persistent Data/CFData reference returned
247 | keychainQueryDictionary[SecReturnPersistentRef] = kCFBooleanTrue
248 |
249 | // Search
250 | var result: AnyObject?
251 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
252 |
253 | return status == noErr ? result as? Data : nil
254 | }
255 |
256 | // MARK: Public Setters
257 |
258 | @discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
259 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
260 | }
261 |
262 | @discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
263 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
264 | }
265 |
266 | @discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
267 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
268 | }
269 |
270 | @discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
271 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
272 | }
273 |
274 | /// Save a String value to the keychain associated with a specified key. If a String value already exists for the given key, the string will be overwritten with the new value.
275 | ///
276 | /// - parameter value: The String value to save.
277 | /// - parameter forKey: The key to save the String under.
278 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
279 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
280 | /// - returns: True if the save was successful, false otherwise.
281 | @discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
282 | if let data = value.data(using: .utf8) {
283 | return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
284 | } else {
285 | return false
286 | }
287 | }
288 |
289 | /// Save an NSCoding compliant object to the keychain associated with a specified key. If an object already exists for the given key, the object will be overwritten with the new value.
290 | ///
291 | /// - parameter value: The NSCoding compliant object to save.
292 | /// - parameter forKey: The key to save the object under.
293 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
294 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
295 | /// - returns: True if the save was successful, false otherwise.
296 | @discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
297 | let data = NSKeyedArchiver.archivedData(withRootObject: value)
298 |
299 | return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
300 | }
301 |
302 | /// Save a Data object to the keychain associated with a specified key. If data already exists for the given key, the data will be overwritten with the new value.
303 | ///
304 | /// - parameter value: The Data object to save.
305 | /// - parameter forKey: The key to save the object under.
306 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
307 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
308 | /// - returns: True if the save was successful, false otherwise.
309 | @discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
310 | var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
311 |
312 | keychainQueryDictionary[SecValueData] = value
313 |
314 | if let accessibility = accessibility {
315 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
316 | } else {
317 | // Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
318 | keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
319 | }
320 |
321 | let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
322 |
323 | if status == errSecSuccess {
324 | return true
325 | } else if status == errSecDuplicateItem {
326 | return update(value, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
327 | } else {
328 | return false
329 | }
330 | }
331 |
332 | @available(*, deprecated, message: "remove is deprecated since version 2.2.1, use removeObject instead")
333 | @discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
334 | return removeObject(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
335 | }
336 |
337 | /// Remove an object associated with a specified key. If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with.
338 | ///
339 | /// - parameter forKey: The key value to remove data for.
340 | /// - parameter withAccessibility: Optional accessibility level to use when looking up the keychain item.
341 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
342 | /// - returns: True if successful, false otherwise.
343 | @discardableResult open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
344 | let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
345 |
346 | // Delete
347 | let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
348 |
349 | if status == errSecSuccess {
350 | return true
351 | } else {
352 | return false
353 | }
354 | }
355 |
356 | /// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set.
357 | @discardableResult open func removeAllKeys() -> Bool {
358 | // Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
359 | var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
360 |
361 | // Uniquely identify this keychain accessor
362 | keychainQueryDictionary[SecAttrService] = serviceName
363 |
364 | // Set the keychain access group if defined
365 | if let accessGroup = self.accessGroup {
366 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
367 | }
368 |
369 | let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
370 |
371 | if status == errSecSuccess {
372 | return true
373 | } else {
374 | return false
375 | }
376 | }
377 |
378 | /// Remove all keychain data, including data not added through keychain wrapper.
379 | ///
380 | /// - Warning: This may remove custom keychain entries you did not add via SwiftKeychainWrapper.
381 | ///
382 | open class func wipeKeychain() {
383 | deleteKeychainSecClass(kSecClassGenericPassword) // Generic password items
384 | deleteKeychainSecClass(kSecClassInternetPassword) // Internet password items
385 | deleteKeychainSecClass(kSecClassCertificate) // Certificate items
386 | deleteKeychainSecClass(kSecClassKey) // Cryptographic key items
387 | deleteKeychainSecClass(kSecClassIdentity) // Identity items
388 | }
389 |
390 | // MARK:- Private Methods
391 |
392 | /// Remove all items for a given Keychain Item Class
393 | ///
394 | ///
395 | @discardableResult private class func deleteKeychainSecClass(_ secClass: AnyObject) -> Bool {
396 | let query = [SecClass: secClass]
397 | let status: OSStatus = SecItemDelete(query as CFDictionary)
398 |
399 | if status == errSecSuccess {
400 | return true
401 | } else {
402 | return false
403 | }
404 | }
405 |
406 | /// Update existing data associated with a specified key name. The existing data will be overwritten by the new data.
407 | private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool {
408 | var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable)
409 | let updateDictionary = [SecValueData:value]
410 |
411 | // on update, only set accessibility if passed in
412 | if let accessibility = accessibility {
413 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
414 | }
415 |
416 | // Update
417 | let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
418 |
419 | if status == errSecSuccess {
420 | return true
421 | } else {
422 | return false
423 | }
424 | }
425 |
426 | /// Setup the keychain query dictionary used to access the keychain on iOS for a specified key name. Takes into account the Service Name and Access Group if one is set.
427 | ///
428 | /// - parameter forKey: The key this query is for
429 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item. If none is provided, will default to .WhenUnlocked
430 | /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false
431 | /// - returns: A dictionary with all the needed properties setup to access the keychain on iOS
432 | private func setupKeychainQueryDictionary(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> [String:Any] {
433 | // Setup default access as generic password (rather than a certificate, internet password, etc)
434 | var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
435 |
436 | // Uniquely identify this keychain accessor
437 | keychainQueryDictionary[SecAttrService] = serviceName
438 |
439 | // Only set accessibiilty if its passed in, we don't want to default it here in case the user didn't want it set
440 | if let accessibility = accessibility {
441 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
442 | }
443 |
444 | // Set the keychain access group if defined
445 | if let accessGroup = self.accessGroup {
446 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
447 | }
448 |
449 | // Uniquely identify the account who will be accessing the keychain
450 | let encodedIdentifier: Data? = key.data(using: String.Encoding.utf8)
451 |
452 | keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
453 |
454 | keychainQueryDictionary[SecAttrAccount] = encodedIdentifier
455 |
456 | keychainQueryDictionary[SecAttrSynchronizable] = isSynchronizable ? kCFBooleanTrue : kCFBooleanFalse
457 |
458 | return keychainQueryDictionary
459 | }
460 | }
461 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/KeychainWrapperSubscript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperSubscript.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Vato Kostava on 5/10/20.
6 | // Copyright © 2020 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | #if canImport(CoreGraphics)
12 | import CoreGraphics
13 | #endif
14 |
15 | public extension KeychainWrapper {
16 |
17 | func remove(forKey key: Key) {
18 | removeObject(forKey: key.rawValue)
19 | }
20 |
21 | }
22 |
23 | public extension KeychainWrapper {
24 |
25 | subscript(key: Key) -> String? {
26 | get { return string(forKey: key) }
27 | set {
28 | guard let value = newValue else { return }
29 | set(value, forKey: key.rawValue)
30 | }
31 | }
32 |
33 | subscript(key: Key) -> Bool? {
34 | get { return bool(forKey: key) }
35 | set {
36 | guard let value = newValue else { return }
37 | set(value, forKey: key.rawValue)
38 | }
39 | }
40 |
41 | subscript(key: Key) -> Int? {
42 | get { return integer(forKey: key) }
43 | set {
44 | guard let value = newValue else { return }
45 | set(value, forKey: key.rawValue)
46 | }
47 | }
48 |
49 | subscript(key: Key) -> Double? {
50 | get { return double(forKey: key) }
51 | set {
52 | guard let value = newValue else { return }
53 | set(value, forKey: key.rawValue)
54 | }
55 | }
56 |
57 | subscript(key: Key) -> Float? {
58 | get { return float(forKey: key) }
59 | set {
60 | guard let value = newValue else { return }
61 | set(value, forKey: key.rawValue)
62 | }
63 | }
64 |
65 | #if canImport(CoreGraphics)
66 | subscript(key: Key) -> CGFloat? {
67 | get { return cgFloat(forKey: key) }
68 | set {
69 | guard let cgValue = newValue else { return }
70 | let value = Float(cgValue)
71 | set(value, forKey: key.rawValue)
72 | }
73 | }
74 | #endif
75 |
76 | subscript(key: Key) -> Data? {
77 | get { return data(forKey: key) }
78 | set {
79 | guard let value = newValue else { return }
80 | set(value, forKey: key.rawValue)
81 | }
82 | }
83 |
84 | }
85 |
86 |
87 | public extension KeychainWrapper {
88 |
89 | func data(forKey key: Key) -> Data? {
90 | if let value = data(forKey: key.rawValue) {
91 | return value
92 | }
93 | return nil
94 | }
95 |
96 | func bool(forKey key: Key) -> Bool? {
97 | if let value = bool(forKey: key.rawValue) {
98 | return value
99 | }
100 |
101 | return nil
102 | }
103 |
104 | func integer(forKey key: Key) -> Int? {
105 | if let value = integer(forKey: key.rawValue) {
106 | return value
107 | }
108 |
109 | return nil
110 | }
111 |
112 | func float(forKey key: Key) -> Float? {
113 | if let value = float(forKey: key.rawValue) {
114 | return value
115 | }
116 |
117 | return nil
118 | }
119 |
120 | #if canImport(CoreGraphics)
121 | func cgFloat(forKey key: Key) -> CGFloat? {
122 | if let value = float(forKey: key) {
123 | return CGFloat(value)
124 | }
125 |
126 | return nil
127 | }
128 | #endif
129 |
130 | func double(forKey key: Key) -> Double? {
131 | if let value = double(forKey: key.rawValue) {
132 | return value
133 | }
134 |
135 | return nil
136 | }
137 |
138 | func string(forKey key: Key) -> String? {
139 | if let value = string(forKey: key.rawValue) {
140 | return value
141 | }
142 |
143 | return nil
144 | }
145 |
146 | }
147 |
148 |
149 | public extension KeychainWrapper {
150 |
151 | struct Key: Hashable, RawRepresentable, ExpressibleByStringLiteral {
152 |
153 | public var rawValue: String
154 |
155 | public init(rawValue: String) {
156 | self.rawValue = rawValue
157 | }
158 |
159 | public init(stringLiteral value: String) {
160 | self.rawValue = value
161 | }
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/SwiftKeychainWrapper.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftKeychainWrapper.h
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 1/13/15.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 | #import
28 |
29 | //! Project version number for SwiftKeychainWrapper.
30 | FOUNDATION_EXPORT double SwiftKeychainWrapperVersionNumber;
31 |
32 | //! Project version string for SwiftKeychainWrapper.
33 | FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperVersionString[];
34 |
35 | // In this header, you should import all the public headers of your framework using statements like #import
36 |
37 |
38 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0.8
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0.8
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperDefaultWrapperTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 8/8/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SwiftKeychainWrapper
11 |
12 | class KeychainWrapperDefaultWrapperTests: XCTestCase {
13 | let testKey = "acessorTestKey"
14 | let testString = "This is a test"
15 |
16 | let testKey2 = "acessorTestKey2"
17 | let testString2 = "Test 2 String"
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 | }
23 |
24 | override func tearDown() {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 |
27 | // clean up keychain
28 | KeychainWrapper.standard.removeObject(forKey: testKey)
29 | KeychainWrapper.standard.removeObject(forKey: testKey2)
30 |
31 | super.tearDown()
32 | }
33 |
34 | func testDefaultServiceName() {
35 | let bundleIdentifier = Bundle.main.bundleIdentifier
36 | if let bundleIdentifierString = bundleIdentifier {
37 | XCTAssertEqual(KeychainWrapper.standard.serviceName, bundleIdentifierString, "Service Name should be equal to the bundle identifier when it is accessible")
38 | } else {
39 | XCTAssertEqual(KeychainWrapper.standard.serviceName, "SwiftKeychainWrapper", "Service Name should be equal to SwiftKeychainWrapper when the bundle identifier is not accessible")
40 | }
41 | }
42 |
43 | func testDefaultAccessGroup() {
44 | XCTAssertNil(KeychainWrapper.standard.accessGroup, "Access Group should be nil when nothing is set")
45 | }
46 |
47 | func testHasValueForKey() {
48 | XCTAssertFalse(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should not have a value for the test key")
49 |
50 | KeychainWrapper.standard.set(testString, forKey: testKey)
51 |
52 | XCTAssertTrue(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should have a value for the test key after it is set")
53 | }
54 |
55 | func testRemoveObjectFromKeychain() {
56 | KeychainWrapper.standard.set(testString, forKey: testKey)
57 |
58 | XCTAssertTrue(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should have a value for the test key after it is set")
59 |
60 | KeychainWrapper.standard.removeObject(forKey: testKey)
61 |
62 | XCTAssertFalse(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should not have a value for the test key after it is removed")
63 | }
64 |
65 | func testStringSave() {
66 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
67 |
68 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
69 |
70 | // clean up keychain
71 | KeychainWrapper.standard.removeObject(forKey: testKey)
72 | }
73 |
74 | func testStringRetrieval() {
75 | KeychainWrapper.standard.set(testString, forKey: testKey)
76 |
77 | if let retrievedString = KeychainWrapper.standard.string(forKey: testKey) {
78 | XCTAssertEqual(retrievedString, testString, "String retrieved for key should equal string saved for key")
79 | } else {
80 | XCTFail("String for Key not found")
81 | }
82 | }
83 |
84 | func testStringRetrievalWhenValueDoesNotExist() {
85 | let retrievedString = KeychainWrapper.standard.string(forKey: testKey)
86 | XCTAssertNil(retrievedString, "String for Key should not exist")
87 | }
88 |
89 | func testMultipleStringSave() {
90 | if !KeychainWrapper.standard.set(testString, forKey: testKey) {
91 | XCTFail("String for testKey did not save")
92 | }
93 |
94 | if !KeychainWrapper.standard.set(testString2, forKey: testKey2) {
95 | XCTFail("String for testKey2 did not save")
96 | }
97 |
98 | if let string1Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
99 | XCTAssertEqual(string1Retrieved, testString, "String retrieved for testKey should match string saved to testKey")
100 | } else {
101 | XCTFail("String for testKey could not be retrieved")
102 | }
103 |
104 | if let string2Retrieved = KeychainWrapper.standard.string(forKey: testKey2) {
105 | XCTAssertEqual(string2Retrieved, testString2, "String retrieved for testKey2 should match string saved to testKey2")
106 | } else {
107 | XCTFail("String for testKey2 could not be retrieved")
108 | }
109 | }
110 |
111 | func testMultipleStringsSavedToSameKey() {
112 |
113 | if !KeychainWrapper.standard.set(testString, forKey: testKey) {
114 | XCTFail("String for testKey did not save")
115 | }
116 |
117 | if let string1Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
118 | XCTAssertEqual(string1Retrieved, testString, "String retrieved for testKey after first save should match first string saved testKey")
119 | } else {
120 | XCTFail("String for testKey could not be retrieved")
121 | }
122 |
123 | if !KeychainWrapper.standard.set(testString2, forKey: testKey) {
124 | XCTFail("String for testKey did not update")
125 | }
126 |
127 | if let string2Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
128 | XCTAssertEqual(string2Retrieved, testString2, "String retrieved for testKey after update should match second string saved to testKey")
129 | } else {
130 | XCTFail("String for testKey could not be retrieved after update")
131 | }
132 | }
133 |
134 | func testNSCodingObjectSave() {
135 | let myTestObject = TestObject()
136 | let objectSaved = KeychainWrapper.standard.set(myTestObject, forKey: testKey)
137 |
138 | XCTAssertTrue(objectSaved, "Object that implements NSCoding should save to Keychain")
139 | }
140 |
141 | func testNSCodingObjectRetrieval() {
142 | let testInt: Int = 9
143 | let myTestObject = TestObject()
144 | myTestObject.objectName = testString
145 | myTestObject.objectRating = testInt
146 |
147 | KeychainWrapper.standard.set(myTestObject, forKey: testKey)
148 |
149 | if let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject{
150 | XCTAssertEqual(retrievedObject.objectName, testString, "NSCoding compliant object retrieved for key should have objectName property equal to what it was stored with")
151 | XCTAssertEqual(retrievedObject.objectRating, testInt, "NSCoding compliant object retrieved for key should have objectRating property equal to what it was stored with")
152 | } else {
153 | XCTFail("Object for Key not found")
154 | }
155 | }
156 |
157 | func testNSCodingObjectRetrievalWhenValueDoesNotExist() {
158 | let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject
159 | XCTAssertNil(retrievedObject, "Object for Key should not exist")
160 | }
161 |
162 | func testDataSave() {
163 | let testData = testString.data(using: String.Encoding.utf8)
164 |
165 | if let data = testData {
166 | let dataSaved = KeychainWrapper.standard.set(data, forKey: testKey)
167 |
168 | XCTAssertTrue(dataSaved, "Data did not save to Keychain")
169 | } else {
170 | XCTFail("Failed to create Data")
171 | }
172 | }
173 |
174 | func testDataRetrieval() {
175 | guard let testData = testString.data(using: String.Encoding.utf8) else {
176 | XCTFail("Failed to create Data")
177 | return
178 | }
179 |
180 | KeychainWrapper.standard.set(testData, forKey: testKey)
181 |
182 | guard let retrievedData = KeychainWrapper.standard.data(forKey: testKey) else {
183 | XCTFail("Data for Key not found")
184 | return
185 | }
186 |
187 | if KeychainWrapper.standard.dataRef(forKey: testKey) == nil {
188 | XCTFail("Data references for Key not found")
189 | }
190 |
191 | if let retrievedString = String(data: retrievedData, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) {
192 | XCTAssertEqual(retrievedString, testString, "String retrieved from data for key should equal string saved as data for key")
193 | } else {
194 | XCTFail("Output Data for key does not match input. ")
195 | }
196 | }
197 |
198 | func testDataRetrievalWhenValueDoesNotExist() {
199 | let retrievedData = KeychainWrapper.standard.data(forKey: testKey)
200 | XCTAssertNil(retrievedData, "Data for Key should not exist")
201 |
202 | let retrievedDataRef = KeychainWrapper.standard.dataRef(forKey: testKey)
203 | XCTAssertNil(retrievedDataRef, "Data ref for Key should not exist")
204 | }
205 |
206 | func testKeysEmpty() {
207 | let keys = KeychainWrapper.standard.allKeys()
208 | XCTAssertEqual(keys, [], "Empty keychain should not contain keys")
209 | }
210 |
211 | func testKeysOneKey() {
212 | let keySuccessfullySet = KeychainWrapper.standard.set(testString, forKey: testKey)
213 |
214 | XCTAssertTrue(keySuccessfullySet, "Setting value on Standard Keychain failed")
215 |
216 | let keys = KeychainWrapper.standard.allKeys()
217 | XCTAssertEqual(keys, [testKey], "Keychain should contain the inserted key")
218 | }
219 |
220 | func testKeysMultipleKeys() {
221 | let keySuccessfullySet = KeychainWrapper.standard.set(testString, forKey: testKey)
222 | XCTAssertTrue(keySuccessfullySet, "Setting value on Standard Keychain failed")
223 |
224 | let key2SuccessfullySet = KeychainWrapper.standard.set(testString2, forKey: testKey2)
225 |
226 | XCTAssertTrue(key2SuccessfullySet, "Setting 2nd value on Standard Keychain failed")
227 |
228 | let keys = KeychainWrapper.standard.allKeys()
229 | XCTAssertEqual(keys, [testKey, testKey2], "Keychain should contain the inserted keys")
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperDeleteTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperDeleteTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 3/25/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SwiftKeychainWrapper
11 |
12 | class KeychainWrapperDeleteTests: XCTestCase {
13 | let testKey = "deleteTestKey"
14 | let testString = "This is a test"
15 |
16 | override func setUp() {
17 | super.setUp()
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 | }
20 |
21 | override func tearDown() {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | super.tearDown()
24 | }
25 |
26 | func testRemoveAllKeysDeletesSpecificKey() {
27 | // save a value we can test delete on
28 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
29 |
30 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
31 |
32 | // delete all
33 | let removeSuccessful = KeychainWrapper.standard.removeAllKeys()
34 |
35 | XCTAssertTrue(removeSuccessful, "Failed to remove all Keys")
36 |
37 | // confirm our test value was deleted
38 | let retrievedValue = KeychainWrapper.standard.string(forKey: testKey)
39 |
40 | XCTAssertNil(retrievedValue, "Test value was not deleted")
41 | }
42 |
43 | func testWipeKeychainDeletesSpecificKey() {
44 | // save a value we can test delete on
45 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
46 |
47 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
48 |
49 | // delete all
50 | KeychainWrapper.wipeKeychain()
51 |
52 | // confirm our test value was deleted
53 | let retrievedValue = KeychainWrapper.standard.string(forKey: testKey)
54 |
55 | XCTAssertNil(retrievedValue, "Test value was not deleted")
56 |
57 | // clean up keychain
58 | KeychainWrapper.standard.removeObject(forKey: testKey)
59 | }
60 |
61 | // func testRemoveAllKeysOnlyRemovesKeysForCurrentServiceName() {
62 | //
63 | // }
64 | //
65 | // func testRemoveAllKeysOnlyRemovesKeysForCurrentAccessGroup() {
66 | //
67 | // }
68 | }
69 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperPrimitiveValueTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperPrimitiveValueTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 4/1/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SwiftKeychainWrapper
11 |
12 | class KeychainWrapperPrimitiveValueTests: XCTestCase {
13 | let testKey = "primitiveValueTestKey"
14 | let testInteger: Int = 42
15 | let testBool: Bool = false
16 | let testFloat: Float = 5.25
17 | let testDouble: Double = 10.75
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 | }
23 |
24 | override func tearDown() {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 | super.tearDown()
27 | }
28 |
29 | func testIntegerSave() {
30 | let valueSaved = KeychainWrapper.standard.set(testInteger, forKey: testKey)
31 |
32 | XCTAssertTrue(valueSaved, "Integer value did not save to Keychain")
33 |
34 | // clean up keychain
35 | KeychainWrapper.standard.removeObject(forKey: testKey)
36 | }
37 |
38 | func testIntegerRetrieval() {
39 | KeychainWrapper.standard.set(testInteger, forKey: testKey)
40 |
41 | if let retrievedValue = KeychainWrapper.standard.integer(forKey: testKey) {
42 | XCTAssertEqual(retrievedValue, testInteger, "Integer value retrieved for key should equal value saved for key")
43 | } else {
44 | XCTFail("Integer value for Key not found")
45 | }
46 | }
47 |
48 | func testBoolSave() {
49 | let valueSaved = KeychainWrapper.standard.set(testBool, forKey: testKey)
50 |
51 | XCTAssertTrue(valueSaved, "Bool value did not save to Keychain")
52 |
53 | // clean up keychain
54 | KeychainWrapper.standard.removeObject(forKey: testKey)
55 | }
56 |
57 | func testBoolRetrieval() {
58 | KeychainWrapper.standard.set(testBool, forKey: testKey)
59 |
60 | if let retrievedValue = KeychainWrapper.standard.bool(forKey: testKey) {
61 | XCTAssertEqual(retrievedValue, testBool, "Bool value retrieved for key should equal value saved for key")
62 | } else {
63 | XCTFail("Bool value for Key not found")
64 | }
65 | }
66 |
67 | func testFloatSave() {
68 | let valueSaved = KeychainWrapper.standard.set(testFloat, forKey: testKey)
69 |
70 | XCTAssertTrue(valueSaved, "Float value did not save to Keychain")
71 |
72 | // clean up keychain
73 | KeychainWrapper.standard.removeObject(forKey: testKey)
74 | }
75 |
76 | func testFloatRetrieval() {
77 | KeychainWrapper.standard.set(testFloat, forKey: testKey)
78 |
79 | if let retrievedValue = KeychainWrapper.standard.float(forKey: testKey) {
80 | XCTAssertEqual(retrievedValue, testFloat, "Float value retrieved for key should equal value saved for key")
81 | } else {
82 | XCTFail("Float value for Key not found")
83 | }
84 | }
85 |
86 | func testDoubleSave() {
87 | let valueSaved = KeychainWrapper.standard.set(testDouble, forKey: testKey)
88 |
89 | XCTAssertTrue(valueSaved, "Double value did not save to Keychain")
90 |
91 | // clean up keychain
92 | KeychainWrapper.standard.removeObject(forKey: testKey)
93 | }
94 |
95 | func testDoubleRetrieval() {
96 | KeychainWrapper.standard.set(testDouble, forKey: testKey)
97 |
98 | if let retrievedValue = KeychainWrapper.standard.double(forKey: testKey) {
99 | XCTAssertEqual(retrievedValue, testDouble, "Double value retrieved for key should equal value saved for key")
100 | } else {
101 | XCTFail("Double value for Key not found")
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 4/25/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SwiftKeychainWrapper
11 |
12 | class KeychainWrapperTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testCustomInstance() {
25 | let uniqueServiceName = UUID().uuidString
26 | let uniqueAccessGroup = UUID().uuidString
27 | let customKeychainWrapperInstance = KeychainWrapper(serviceName: uniqueServiceName, accessGroup: uniqueAccessGroup)
28 |
29 | XCTAssertNotEqual(customKeychainWrapperInstance.serviceName, KeychainWrapper.standard.serviceName, "Custom instance initialized with unique service name, should not match standard Service Name")
30 | XCTAssertNotEqual(customKeychainWrapperInstance.accessGroup, KeychainWrapper.standard.accessGroup, "Custom instance initialized with unique access group, should not match standard Access Group")
31 | }
32 |
33 | func testAccessibility() {
34 | let accessibilityOptions: [KeychainItemAccessibility] = [
35 | .afterFirstUnlock,
36 | .afterFirstUnlockThisDeviceOnly,
37 | .always,
38 | .whenPasscodeSetThisDeviceOnly,
39 | .alwaysThisDeviceOnly,
40 | .whenUnlocked,
41 | .whenUnlockedThisDeviceOnly
42 | ]
43 |
44 | let key = "testKey"
45 |
46 | for accessibilityOption in accessibilityOptions {
47 | KeychainWrapper.standard.set("Test123", forKey: key, withAccessibility: accessibilityOption)
48 |
49 | let accessibilityForKey = KeychainWrapper.standard.accessibilityOfKey(key)
50 |
51 | let accessibilityDescription = String(describing: accessibilityForKey)
52 |
53 | XCTAssertEqual(accessibilityForKey, accessibilityOption, "Accessibility does not match. Expected: \(accessibilityOption) Found: \(accessibilityDescription)")
54 |
55 | // INFO: If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with
56 | KeychainWrapper.standard.removeObject(forKey: key, withAccessibility: accessibilityOption)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/TestObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // testObject.swift
3 | // KeychainWrapper
4 | //
5 | // Created by Jason Rendel on 9/23/14.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 | class TestObject: NSObject, NSCoding {
31 | var objectName = "Name"
32 | var objectRating = 0
33 |
34 | override init() { }
35 |
36 | required init?(coder decoder: NSCoder) {
37 | if let name = decoder.decodeObject(forKey: "objectName") as? String {
38 | self.objectName = name
39 | }
40 |
41 | self.objectRating = decoder.decodeInteger(forKey: "objectRating")
42 | }
43 |
44 | func encode(with encoder: NSCoder) {
45 | encoder.encode(self.objectName, forKey: "objectName")
46 | encoder.encode(self.objectRating, forKey: "objectRating")
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/TestHostApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TestHostApp
4 | //
5 | // Created by Jason Rendel on 11/4/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // 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.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // 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.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // 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.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/TestHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/TestHostApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/TestHostApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/TestHostApp/Debug.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | get-task-allow
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TestHostApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/TestHostApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // TestHostApp
4 | //
5 | // Created by Jason Rendel on 11/4/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------