├── .gitignore ├── .travis.yml ├── LICENSE ├── LockBox ├── Default-568h@2x.png ├── LockBox.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ ├── LockBox app.xcscheme │ │ ├── LockBox.xcscheme │ │ └── UnitTests.xcscheme ├── LockBox │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Info.plist │ ├── LockBox-Info.plist │ ├── LockBox-Prefix.pch │ ├── LockBox.entitlements │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ └── ViewController.xib │ └── main.m └── UnitTests │ ├── Lockbox3Tests.m │ ├── LockboxTests.m │ ├── UnitTests-Info.plist │ ├── UnitTests-Prefix.pch │ └── en.lproj │ └── InfoPlist.strings ├── Lockbox.h ├── Lockbox.m ├── Lockbox.podspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Emacs 2 | *~ 3 | 4 | # Xcode 5 | .DS_Store 6 | build/ 7 | .idea/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | #*.xcworkspace 17 | !default.xcworkspace 18 | xcuserdata 19 | profile 20 | *.moved-aside 21 | DerivedData 22 | */xcshareddata/* 23 | *.xccheckout 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | script: xcodebuild -project LockBox/LockBox.xcodeproj/ -sdk iphonesimulator -scheme LockBox ONLY_ACTIVE_ARCH=NO build 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Hawk iMedia, All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /LockBox/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/granoff/Lockbox/5fcd476c4a1e9f6b33304d230b604d8cf3598f65/LockBox/Default-568h@2x.png -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D0B9E641540556E00E5BE71 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E631540556E00E5BE71 /* UIKit.framework */; }; 11 | 2D0B9E661540556E00E5BE71 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E651540556E00E5BE71 /* Foundation.framework */; }; 12 | 2D0B9E681540556E00E5BE71 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E671540556E00E5BE71 /* CoreGraphics.framework */; }; 13 | 2D0B9E6E1540556E00E5BE71 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2D0B9E6C1540556E00E5BE71 /* InfoPlist.strings */; }; 14 | 2D0B9E701540556E00E5BE71 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B9E6F1540556E00E5BE71 /* main.m */; }; 15 | 2D0B9E741540556E00E5BE71 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B9E731540556E00E5BE71 /* AppDelegate.m */; }; 16 | 2D0B9E771540556E00E5BE71 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B9E761540556E00E5BE71 /* ViewController.m */; }; 17 | 2D0B9E7A1540556E00E5BE71 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2D0B9E781540556E00E5BE71 /* ViewController.xib */; }; 18 | 2D0B9E821540558B00E5BE71 /* Lockbox.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B9E811540558B00E5BE71 /* Lockbox.m */; }; 19 | 2D0B9E841540565000E5BE71 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E831540565000E5BE71 /* Security.framework */; }; 20 | 4D6801EB16B478CB000CED0E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E631540556E00E5BE71 /* UIKit.framework */; }; 21 | 4D6801EC16B478CB000CED0E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0B9E651540556E00E5BE71 /* Foundation.framework */; }; 22 | 4D6801F216B478CB000CED0E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4D6801F016B478CB000CED0E /* InfoPlist.strings */; }; 23 | 4D6801FD16B478F7000CED0E /* LockboxTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6801FC16B478F7000CED0E /* LockboxTests.m */; }; 24 | 4D68021016B489AB000CED0E /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 4D68020D16B489AB000CED0E /* LICENSE */; }; 25 | 4D68021116B489AB000CED0E /* Lockbox.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 4D68020E16B489AB000CED0E /* Lockbox.podspec */; }; 26 | 4D68021216B489AB000CED0E /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 4D68020F16B489AB000CED0E /* README.md */; }; 27 | 4D88390B2389965300CC55E8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D88390A2389965300CC55E8 /* Security.framework */; }; 28 | 4D917A0A1C593EC800323832 /* Lockbox3Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D917A091C593EC800323832 /* Lockbox3Tests.m */; }; 29 | 4DE9819617FF3C3000E7D452 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DE9819517FF3C3000E7D452 /* Default-568h@2x.png */; }; 30 | FAD106491D961E9700F81263 /* Lockbox.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B9E811540558B00E5BE71 /* Lockbox.m */; }; 31 | FAD1064B1D96208400F81263 /* Lockbox.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0B9E801540558B00E5BE71 /* Lockbox.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXContainerItemProxy section */ 35 | 4D68020116B47B88000CED0E /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 2D0B9E561540556D00E5BE71 /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 2D0B9E5E1540556E00E5BE71; 40 | remoteInfo = LockBox; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 2D0B9E5F1540556E00E5BE71 /* LockBox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LockBox.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 2D0B9E631540556E00E5BE71 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 47 | 2D0B9E651540556E00E5BE71 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 48 | 2D0B9E671540556E00E5BE71 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 49 | 2D0B9E6B1540556E00E5BE71 /* LockBox-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LockBox-Info.plist"; sourceTree = ""; }; 50 | 2D0B9E6D1540556E00E5BE71 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 51 | 2D0B9E6F1540556E00E5BE71 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = main.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 52 | 2D0B9E711540556E00E5BE71 /* LockBox-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LockBox-Prefix.pch"; sourceTree = ""; }; 53 | 2D0B9E721540556E00E5BE71 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppDelegate.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 54 | 2D0B9E731540556E00E5BE71 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 55 | 2D0B9E751540556E00E5BE71 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 56 | 2D0B9E761540556E00E5BE71 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 57 | 2D0B9E791540556E00E5BE71 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController.xib; sourceTree = ""; }; 58 | 2D0B9E801540558B00E5BE71 /* Lockbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = Lockbox.h; path = ../../Lockbox.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 59 | 2D0B9E811540558B00E5BE71 /* Lockbox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = Lockbox.m; path = ../../Lockbox.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 60 | 2D0B9E831540565000E5BE71 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 61 | 4D6801E816B478CB000CED0E /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 4D6801EF16B478CB000CED0E /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; 63 | 4D6801F116B478CB000CED0E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 64 | 4D6801F616B478CB000CED0E /* UnitTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UnitTests-Prefix.pch"; sourceTree = ""; }; 65 | 4D6801FC16B478F7000CED0E /* LockboxTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LockboxTests.m; sourceTree = ""; }; 66 | 4D68020D16B489AB000CED0E /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 67 | 4D68020E16B489AB000CED0E /* Lockbox.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Lockbox.podspec; path = ../Lockbox.podspec; sourceTree = ""; }; 68 | 4D68020F16B489AB000CED0E /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = ""; }; 69 | 4D88390A2389965300CC55E8 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 70 | 4D917A091C593EC800323832 /* Lockbox3Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Lockbox3Tests.m; sourceTree = ""; }; 71 | 4DA5A23C1D897F9D0072168E /* LockBox.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LockBox.entitlements; sourceTree = ""; }; 72 | 4DE9819517FF3C3000E7D452 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 73 | FAD106411D961E8900F81263 /* Lockbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Lockbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | FAD106431D961E8900F81263 /* Lockbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Lockbox.h; sourceTree = ""; }; 75 | FAD106441D961E8900F81263 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | 2D0B9E5C1540556E00E5BE71 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 2D0B9E841540565000E5BE71 /* Security.framework in Frameworks */, 84 | 2D0B9E641540556E00E5BE71 /* UIKit.framework in Frameworks */, 85 | 2D0B9E661540556E00E5BE71 /* Foundation.framework in Frameworks */, 86 | 2D0B9E681540556E00E5BE71 /* CoreGraphics.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 4D6801E416B478CB000CED0E /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | 4D88390B2389965300CC55E8 /* Security.framework in Frameworks */, 95 | 4D6801EB16B478CB000CED0E /* UIKit.framework in Frameworks */, 96 | 4D6801EC16B478CB000CED0E /* Foundation.framework in Frameworks */, 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | FAD1063D1D961E8900F81263 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | /* End PBXFrameworksBuildPhase section */ 108 | 109 | /* Begin PBXGroup section */ 110 | 2D0B9E541540556D00E5BE71 = { 111 | isa = PBXGroup; 112 | children = ( 113 | 4DE9819517FF3C3000E7D452 /* Default-568h@2x.png */, 114 | 2D0B9E691540556E00E5BE71 /* LockBox */, 115 | 4D6801ED16B478CB000CED0E /* UnitTests */, 116 | FAD106421D961E8900F81263 /* Lockbox */, 117 | 2D0B9E621540556E00E5BE71 /* Frameworks */, 118 | 2D0B9E601540556E00E5BE71 /* Products */, 119 | 4D68020C16B4896F000CED0E /* Misc Files */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | 2D0B9E601540556E00E5BE71 /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 2D0B9E5F1540556E00E5BE71 /* LockBox.app */, 127 | 4D6801E816B478CB000CED0E /* UnitTests.xctest */, 128 | FAD106411D961E8900F81263 /* Lockbox.framework */, 129 | ); 130 | name = Products; 131 | sourceTree = ""; 132 | }; 133 | 2D0B9E621540556E00E5BE71 /* Frameworks */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 2D0B9E831540565000E5BE71 /* Security.framework */, 137 | 4D88390A2389965300CC55E8 /* Security.framework */, 138 | 2D0B9E631540556E00E5BE71 /* UIKit.framework */, 139 | 2D0B9E651540556E00E5BE71 /* Foundation.framework */, 140 | 2D0B9E671540556E00E5BE71 /* CoreGraphics.framework */, 141 | ); 142 | name = Frameworks; 143 | sourceTree = ""; 144 | }; 145 | 2D0B9E691540556E00E5BE71 /* LockBox */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 4DA5A23C1D897F9D0072168E /* LockBox.entitlements */, 149 | 2D0B9E801540558B00E5BE71 /* Lockbox.h */, 150 | 2D0B9E811540558B00E5BE71 /* Lockbox.m */, 151 | 2D0B9E721540556E00E5BE71 /* AppDelegate.h */, 152 | 2D0B9E731540556E00E5BE71 /* AppDelegate.m */, 153 | 2D0B9E751540556E00E5BE71 /* ViewController.h */, 154 | 2D0B9E761540556E00E5BE71 /* ViewController.m */, 155 | 2D0B9E781540556E00E5BE71 /* ViewController.xib */, 156 | 2D0B9E6A1540556E00E5BE71 /* Supporting Files */, 157 | ); 158 | path = LockBox; 159 | sourceTree = ""; 160 | }; 161 | 2D0B9E6A1540556E00E5BE71 /* Supporting Files */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 2D0B9E6B1540556E00E5BE71 /* LockBox-Info.plist */, 165 | 2D0B9E6C1540556E00E5BE71 /* InfoPlist.strings */, 166 | 2D0B9E6F1540556E00E5BE71 /* main.m */, 167 | 2D0B9E711540556E00E5BE71 /* LockBox-Prefix.pch */, 168 | ); 169 | name = "Supporting Files"; 170 | sourceTree = ""; 171 | }; 172 | 4D6801ED16B478CB000CED0E /* UnitTests */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 4D6801EE16B478CB000CED0E /* Supporting Files */, 176 | 4D6801FC16B478F7000CED0E /* LockboxTests.m */, 177 | 4D917A091C593EC800323832 /* Lockbox3Tests.m */, 178 | ); 179 | path = UnitTests; 180 | sourceTree = ""; 181 | }; 182 | 4D6801EE16B478CB000CED0E /* Supporting Files */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 4D6801EF16B478CB000CED0E /* UnitTests-Info.plist */, 186 | 4D6801F016B478CB000CED0E /* InfoPlist.strings */, 187 | 4D6801F616B478CB000CED0E /* UnitTests-Prefix.pch */, 188 | ); 189 | name = "Supporting Files"; 190 | sourceTree = ""; 191 | }; 192 | 4D68020C16B4896F000CED0E /* Misc Files */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 4D68020D16B489AB000CED0E /* LICENSE */, 196 | 4D68020E16B489AB000CED0E /* Lockbox.podspec */, 197 | 4D68020F16B489AB000CED0E /* README.md */, 198 | ); 199 | name = "Misc Files"; 200 | sourceTree = ""; 201 | }; 202 | FAD106421D961E8900F81263 /* Lockbox */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | FAD106431D961E8900F81263 /* Lockbox.h */, 206 | FAD106441D961E8900F81263 /* Info.plist */, 207 | ); 208 | path = Lockbox; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXGroup section */ 212 | 213 | /* Begin PBXHeadersBuildPhase section */ 214 | FAD1063E1D961E8900F81263 /* Headers */ = { 215 | isa = PBXHeadersBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | FAD1064B1D96208400F81263 /* Lockbox.h in Headers */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXHeadersBuildPhase section */ 223 | 224 | /* Begin PBXNativeTarget section */ 225 | 2D0B9E5E1540556E00E5BE71 /* LockBox */ = { 226 | isa = PBXNativeTarget; 227 | buildConfigurationList = 2D0B9E7D1540556E00E5BE71 /* Build configuration list for PBXNativeTarget "LockBox" */; 228 | buildPhases = ( 229 | 2D0B9E5B1540556E00E5BE71 /* Sources */, 230 | 2D0B9E5C1540556E00E5BE71 /* Frameworks */, 231 | 2D0B9E5D1540556E00E5BE71 /* Resources */, 232 | ); 233 | buildRules = ( 234 | ); 235 | dependencies = ( 236 | ); 237 | name = LockBox; 238 | productName = LockBox; 239 | productReference = 2D0B9E5F1540556E00E5BE71 /* LockBox.app */; 240 | productType = "com.apple.product-type.application"; 241 | }; 242 | 4D6801E716B478CB000CED0E /* UnitTests */ = { 243 | isa = PBXNativeTarget; 244 | buildConfigurationList = 4D6801F916B478CB000CED0E /* Build configuration list for PBXNativeTarget "UnitTests" */; 245 | buildPhases = ( 246 | 4D6801E316B478CB000CED0E /* Sources */, 247 | 4D6801E416B478CB000CED0E /* Frameworks */, 248 | 4D6801E516B478CB000CED0E /* Resources */, 249 | ); 250 | buildRules = ( 251 | ); 252 | dependencies = ( 253 | 4D68020216B47B88000CED0E /* PBXTargetDependency */, 254 | ); 255 | name = UnitTests; 256 | productName = UnitTests; 257 | productReference = 4D6801E816B478CB000CED0E /* UnitTests.xctest */; 258 | productType = "com.apple.product-type.bundle.unit-test"; 259 | }; 260 | FAD106401D961E8900F81263 /* Lockbox */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = FAD106481D961E8900F81263 /* Build configuration list for PBXNativeTarget "Lockbox" */; 263 | buildPhases = ( 264 | FAD1063C1D961E8900F81263 /* Sources */, 265 | FAD1063D1D961E8900F81263 /* Frameworks */, 266 | FAD1063E1D961E8900F81263 /* Headers */, 267 | FAD1063F1D961E8900F81263 /* Resources */, 268 | ); 269 | buildRules = ( 270 | ); 271 | dependencies = ( 272 | ); 273 | name = Lockbox; 274 | productName = Lockbox; 275 | productReference = FAD106411D961E8900F81263 /* Lockbox.framework */; 276 | productType = "com.apple.product-type.framework"; 277 | }; 278 | /* End PBXNativeTarget section */ 279 | 280 | /* Begin PBXProject section */ 281 | 2D0B9E561540556D00E5BE71 /* Project object */ = { 282 | isa = PBXProject; 283 | attributes = { 284 | LastTestingUpgradeCheck = 0600; 285 | LastUpgradeCheck = 0800; 286 | ORGANIZATIONNAME = "Hawk iMedia"; 287 | TargetAttributes = { 288 | 2D0B9E5E1540556E00E5BE71 = { 289 | DevelopmentTeam = UH5GSL36AW; 290 | SystemCapabilities = { 291 | com.apple.Keychain = { 292 | enabled = 1; 293 | }; 294 | }; 295 | }; 296 | FAD106401D961E8900F81263 = { 297 | CreatedOnToolsVersion = 8.0; 298 | ProvisioningStyle = Automatic; 299 | }; 300 | }; 301 | }; 302 | buildConfigurationList = 2D0B9E591540556D00E5BE71 /* Build configuration list for PBXProject "LockBox" */; 303 | compatibilityVersion = "Xcode 3.2"; 304 | developmentRegion = English; 305 | hasScannedForEncodings = 0; 306 | knownRegions = ( 307 | English, 308 | en, 309 | ); 310 | mainGroup = 2D0B9E541540556D00E5BE71; 311 | productRefGroup = 2D0B9E601540556E00E5BE71 /* Products */; 312 | projectDirPath = ""; 313 | projectRoot = ""; 314 | targets = ( 315 | 2D0B9E5E1540556E00E5BE71 /* LockBox */, 316 | 4D6801E716B478CB000CED0E /* UnitTests */, 317 | FAD106401D961E8900F81263 /* Lockbox */, 318 | ); 319 | }; 320 | /* End PBXProject section */ 321 | 322 | /* Begin PBXResourcesBuildPhase section */ 323 | 2D0B9E5D1540556E00E5BE71 /* Resources */ = { 324 | isa = PBXResourcesBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | 2D0B9E6E1540556E00E5BE71 /* InfoPlist.strings in Resources */, 328 | 2D0B9E7A1540556E00E5BE71 /* ViewController.xib in Resources */, 329 | 4D68021016B489AB000CED0E /* LICENSE in Resources */, 330 | 4D68021116B489AB000CED0E /* Lockbox.podspec in Resources */, 331 | 4D68021216B489AB000CED0E /* README.md in Resources */, 332 | 4DE9819617FF3C3000E7D452 /* Default-568h@2x.png in Resources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 4D6801E516B478CB000CED0E /* Resources */ = { 337 | isa = PBXResourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 4D6801F216B478CB000CED0E /* InfoPlist.strings in Resources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | FAD1063F1D961E8900F81263 /* Resources */ = { 345 | isa = PBXResourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | /* End PBXResourcesBuildPhase section */ 352 | 353 | /* Begin PBXSourcesBuildPhase section */ 354 | 2D0B9E5B1540556E00E5BE71 /* Sources */ = { 355 | isa = PBXSourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | 2D0B9E701540556E00E5BE71 /* main.m in Sources */, 359 | 2D0B9E741540556E00E5BE71 /* AppDelegate.m in Sources */, 360 | 2D0B9E771540556E00E5BE71 /* ViewController.m in Sources */, 361 | 2D0B9E821540558B00E5BE71 /* Lockbox.m in Sources */, 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | }; 365 | 4D6801E316B478CB000CED0E /* Sources */ = { 366 | isa = PBXSourcesBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | 4D6801FD16B478F7000CED0E /* LockboxTests.m in Sources */, 370 | 4D917A0A1C593EC800323832 /* Lockbox3Tests.m in Sources */, 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | FAD1063C1D961E8900F81263 /* Sources */ = { 375 | isa = PBXSourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | FAD106491D961E9700F81263 /* Lockbox.m in Sources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | /* End PBXSourcesBuildPhase section */ 383 | 384 | /* Begin PBXTargetDependency section */ 385 | 4D68020216B47B88000CED0E /* PBXTargetDependency */ = { 386 | isa = PBXTargetDependency; 387 | target = 2D0B9E5E1540556E00E5BE71 /* LockBox */; 388 | targetProxy = 4D68020116B47B88000CED0E /* PBXContainerItemProxy */; 389 | }; 390 | /* End PBXTargetDependency section */ 391 | 392 | /* Begin PBXVariantGroup section */ 393 | 2D0B9E6C1540556E00E5BE71 /* InfoPlist.strings */ = { 394 | isa = PBXVariantGroup; 395 | children = ( 396 | 2D0B9E6D1540556E00E5BE71 /* en */, 397 | ); 398 | name = InfoPlist.strings; 399 | sourceTree = ""; 400 | }; 401 | 2D0B9E781540556E00E5BE71 /* ViewController.xib */ = { 402 | isa = PBXVariantGroup; 403 | children = ( 404 | 2D0B9E791540556E00E5BE71 /* en */, 405 | ); 406 | name = ViewController.xib; 407 | sourceTree = ""; 408 | }; 409 | 4D6801F016B478CB000CED0E /* InfoPlist.strings */ = { 410 | isa = PBXVariantGroup; 411 | children = ( 412 | 4D6801F116B478CB000CED0E /* en */, 413 | ); 414 | name = InfoPlist.strings; 415 | sourceTree = ""; 416 | }; 417 | /* End PBXVariantGroup section */ 418 | 419 | /* Begin XCBuildConfiguration section */ 420 | 2D0B9E7B1540556E00E5BE71 /* Debug */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ALWAYS_SEARCH_USER_PATHS = NO; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INFINITE_RECURSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 432 | CLANG_WARN_UNREACHABLE_CODE = YES; 433 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 434 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 435 | COPY_PHASE_STRIP = NO; 436 | ENABLE_BITCODE = YES; 437 | ENABLE_STRICT_OBJC_MSGSEND = YES; 438 | ENABLE_TESTABILITY = YES; 439 | GCC_C_LANGUAGE_STANDARD = gnu99; 440 | GCC_DYNAMIC_NO_PIC = NO; 441 | GCC_NO_COMMON_BLOCKS = YES; 442 | GCC_OPTIMIZATION_LEVEL = 0; 443 | GCC_PREPROCESSOR_DEFINITIONS = ( 444 | "DEBUG=1", 445 | "$(inherited)", 446 | ); 447 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 448 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 449 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 450 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 451 | GCC_WARN_UNDECLARED_SELECTOR = YES; 452 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 453 | GCC_WARN_UNUSED_FUNCTION = YES; 454 | GCC_WARN_UNUSED_VARIABLE = YES; 455 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 456 | ONLY_ACTIVE_ARCH = YES; 457 | SDKROOT = iphoneos; 458 | }; 459 | name = Debug; 460 | }; 461 | 2D0B9E7C1540556E00E5BE71 /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | ALWAYS_SEARCH_USER_PATHS = NO; 465 | CLANG_ENABLE_OBJC_ARC = YES; 466 | CLANG_WARN_BOOL_CONVERSION = YES; 467 | CLANG_WARN_CONSTANT_CONVERSION = YES; 468 | CLANG_WARN_EMPTY_BODY = YES; 469 | CLANG_WARN_ENUM_CONVERSION = YES; 470 | CLANG_WARN_INFINITE_RECURSION = YES; 471 | CLANG_WARN_INT_CONVERSION = YES; 472 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 473 | CLANG_WARN_UNREACHABLE_CODE = YES; 474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 475 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 476 | COPY_PHASE_STRIP = YES; 477 | ENABLE_BITCODE = YES; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | GCC_C_LANGUAGE_STANDARD = gnu99; 480 | GCC_NO_COMMON_BLOCKS = YES; 481 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 482 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 483 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 484 | GCC_WARN_UNDECLARED_SELECTOR = YES; 485 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 486 | GCC_WARN_UNUSED_FUNCTION = YES; 487 | GCC_WARN_UNUSED_VARIABLE = YES; 488 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 489 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 490 | SDKROOT = iphoneos; 491 | VALIDATE_PRODUCT = YES; 492 | }; 493 | name = Release; 494 | }; 495 | 2D0B9E7E1540556E00E5BE71 /* Debug */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | CLANG_ENABLE_OBJC_ARC = YES; 499 | CODE_SIGN_ENTITLEMENTS = LockBox/LockBox.entitlements; 500 | DEVELOPMENT_TEAM = UH5GSL36AW; 501 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 502 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 503 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 504 | GCC_PREFIX_HEADER = "LockBox/LockBox-Prefix.pch"; 505 | INFOPLIST_FILE = "LockBox/LockBox-Info.plist"; 506 | PRODUCT_BUNDLE_IDENTIFIER = "com.hawkimedia.${PRODUCT_NAME:rfc1034identifier}"; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | WRAPPER_EXTENSION = app; 509 | }; 510 | name = Debug; 511 | }; 512 | 2D0B9E7F1540556E00E5BE71 /* Release */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | CLANG_ENABLE_OBJC_ARC = YES; 516 | CODE_SIGN_ENTITLEMENTS = LockBox/LockBox.entitlements; 517 | DEVELOPMENT_TEAM = UH5GSL36AW; 518 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 519 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 520 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 521 | GCC_PREFIX_HEADER = "LockBox/LockBox-Prefix.pch"; 522 | INFOPLIST_FILE = "LockBox/LockBox-Info.plist"; 523 | PRODUCT_BUNDLE_IDENTIFIER = "com.hawkimedia.${PRODUCT_NAME:rfc1034identifier}"; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | WRAPPER_EXTENSION = app; 526 | }; 527 | name = Release; 528 | }; 529 | 4D6801F716B478CB000CED0E /* Debug */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/LockBox.app/LockBox"; 533 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 534 | CLANG_CXX_LIBRARY = "libc++"; 535 | CLANG_ENABLE_OBJC_ARC = YES; 536 | CLANG_WARN_EMPTY_BODY = YES; 537 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 538 | FRAMEWORK_SEARCH_PATHS = ""; 539 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 540 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 541 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 542 | GCC_PREFIX_HEADER = "UnitTests/UnitTests-Prefix.pch"; 543 | INFOPLIST_FILE = "UnitTests/UnitTests-Info.plist"; 544 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 545 | LIBRARY_SEARCH_PATHS = ""; 546 | ONLY_ACTIVE_ARCH = YES; 547 | OTHER_LDFLAGS = ( 548 | "-Objc", 549 | "-framework", 550 | XCTest, 551 | ); 552 | PRODUCT_BUNDLE_IDENTIFIER = "com.hawkimedia.${PRODUCT_NAME:rfc1034identifier}"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | TEST_HOST = "$(BUNDLE_LOADER)"; 555 | }; 556 | name = Debug; 557 | }; 558 | 4D6801F816B478CB000CED0E /* Release */ = { 559 | isa = XCBuildConfiguration; 560 | buildSettings = { 561 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/LockBox.app/LockBox"; 562 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 563 | CLANG_CXX_LIBRARY = "libc++"; 564 | CLANG_ENABLE_OBJC_ARC = YES; 565 | CLANG_WARN_EMPTY_BODY = YES; 566 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 567 | FRAMEWORK_SEARCH_PATHS = ""; 568 | GCC_GENERATE_TEST_COVERAGE_FILES = YES; 569 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; 570 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 571 | GCC_PREFIX_HEADER = "UnitTests/UnitTests-Prefix.pch"; 572 | INFOPLIST_FILE = "UnitTests/UnitTests-Info.plist"; 573 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 574 | LIBRARY_SEARCH_PATHS = ""; 575 | OTHER_LDFLAGS = ( 576 | "-Objc", 577 | "-framework", 578 | XCTest, 579 | ); 580 | PRODUCT_BUNDLE_IDENTIFIER = "com.hawkimedia.${PRODUCT_NAME:rfc1034identifier}"; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | TEST_HOST = "$(BUNDLE_LOADER)"; 583 | }; 584 | name = Release; 585 | }; 586 | FAD106461D961E8900F81263 /* Debug */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | CLANG_ANALYZER_NONNULL = YES; 590 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 591 | CLANG_CXX_LIBRARY = "libc++"; 592 | CLANG_ENABLE_MODULES = YES; 593 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 594 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 595 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 596 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 597 | CODE_SIGN_IDENTITY = "iPhone Developer"; 598 | CURRENT_PROJECT_VERSION = 1; 599 | DEBUG_INFORMATION_FORMAT = dwarf; 600 | DEFINES_MODULE = YES; 601 | DEVELOPMENT_TEAM = ""; 602 | DYLIB_COMPATIBILITY_VERSION = 1; 603 | DYLIB_CURRENT_VERSION = 1; 604 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 605 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 606 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 607 | INFOPLIST_FILE = Lockbox/Info.plist; 608 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 609 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 610 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 611 | MTL_ENABLE_DEBUG_INFO = YES; 612 | PRODUCT_BUNDLE_IDENTIFIER = com.hawkimedia.Lockbox; 613 | PRODUCT_NAME = "$(TARGET_NAME)"; 614 | SKIP_INSTALL = YES; 615 | TARGETED_DEVICE_FAMILY = "1,2"; 616 | VERSIONING_SYSTEM = "apple-generic"; 617 | VERSION_INFO_PREFIX = ""; 618 | }; 619 | name = Debug; 620 | }; 621 | FAD106471D961E8900F81263 /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | CLANG_ANALYZER_NONNULL = YES; 625 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 626 | CLANG_CXX_LIBRARY = "libc++"; 627 | CLANG_ENABLE_MODULES = YES; 628 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 629 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 630 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 631 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 632 | CODE_SIGN_IDENTITY = "iPhone Developer"; 633 | COPY_PHASE_STRIP = NO; 634 | CURRENT_PROJECT_VERSION = 1; 635 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 636 | DEFINES_MODULE = YES; 637 | DEVELOPMENT_TEAM = ""; 638 | DYLIB_COMPATIBILITY_VERSION = 1; 639 | DYLIB_CURRENT_VERSION = 1; 640 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 641 | ENABLE_NS_ASSERTIONS = NO; 642 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 643 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 644 | INFOPLIST_FILE = Lockbox/Info.plist; 645 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 646 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 647 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 648 | MTL_ENABLE_DEBUG_INFO = NO; 649 | PRODUCT_BUNDLE_IDENTIFIER = com.hawkimedia.Lockbox; 650 | PRODUCT_NAME = "$(TARGET_NAME)"; 651 | SKIP_INSTALL = YES; 652 | TARGETED_DEVICE_FAMILY = "1,2"; 653 | VERSIONING_SYSTEM = "apple-generic"; 654 | VERSION_INFO_PREFIX = ""; 655 | }; 656 | name = Release; 657 | }; 658 | /* End XCBuildConfiguration section */ 659 | 660 | /* Begin XCConfigurationList section */ 661 | 2D0B9E591540556D00E5BE71 /* Build configuration list for PBXProject "LockBox" */ = { 662 | isa = XCConfigurationList; 663 | buildConfigurations = ( 664 | 2D0B9E7B1540556E00E5BE71 /* Debug */, 665 | 2D0B9E7C1540556E00E5BE71 /* Release */, 666 | ); 667 | defaultConfigurationIsVisible = 0; 668 | defaultConfigurationName = Release; 669 | }; 670 | 2D0B9E7D1540556E00E5BE71 /* Build configuration list for PBXNativeTarget "LockBox" */ = { 671 | isa = XCConfigurationList; 672 | buildConfigurations = ( 673 | 2D0B9E7E1540556E00E5BE71 /* Debug */, 674 | 2D0B9E7F1540556E00E5BE71 /* Release */, 675 | ); 676 | defaultConfigurationIsVisible = 0; 677 | defaultConfigurationName = Release; 678 | }; 679 | 4D6801F916B478CB000CED0E /* Build configuration list for PBXNativeTarget "UnitTests" */ = { 680 | isa = XCConfigurationList; 681 | buildConfigurations = ( 682 | 4D6801F716B478CB000CED0E /* Debug */, 683 | 4D6801F816B478CB000CED0E /* Release */, 684 | ); 685 | defaultConfigurationIsVisible = 0; 686 | defaultConfigurationName = Release; 687 | }; 688 | FAD106481D961E8900F81263 /* Build configuration list for PBXNativeTarget "Lockbox" */ = { 689 | isa = XCConfigurationList; 690 | buildConfigurations = ( 691 | FAD106461D961E8900F81263 /* Debug */, 692 | FAD106471D961E8900F81263 /* Release */, 693 | ); 694 | defaultConfigurationIsVisible = 0; 695 | defaultConfigurationName = Release; 696 | }; 697 | /* End XCConfigurationList section */ 698 | }; 699 | rootObject = 2D0B9E561540556D00E5BE71 /* Project object */; 700 | } 701 | -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/xcshareddata/xcschemes/LockBox app.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/xcshareddata/xcschemes/LockBox.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /LockBox/LockBox.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /LockBox/LockBox/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LockBox 4 | // 5 | // Created by Mark H. Granoff on 4/19/12. 6 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ViewController; 12 | 13 | @interface AppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) ViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LockBox/LockBox/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LockBox 4 | // 5 | // Created by Mark H. Granoff on 4/19/12. 6 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | #import "ViewController.h" 12 | 13 | @implementation AppDelegate 14 | 15 | @synthesize window = _window; 16 | @synthesize viewController = _viewController; 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 19 | { 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | // Override point for customization after application launch. 22 | ViewController *vc = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; 23 | self.viewController = vc; 24 | self.window.rootViewController = vc; 25 | [self.window makeKeyAndVisible]; 26 | return YES; 27 | } 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application 30 | { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 33 | } 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application 36 | { 37 | // 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. 38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 39 | } 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application 42 | { 43 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application 47 | { 48 | // 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. 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application 52 | { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /LockBox/LockBox/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 | 3.0.3 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LockBox/LockBox/LockBox-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /LockBox/LockBox/LockBox-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'LockBox' target in the 'LockBox' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /LockBox/LockBox/LockBox.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | keychain-access-groups 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LockBox/LockBox/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LockBox 4 | // 5 | // Created by Mark H. Granoff on 4/19/12. 6 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @property ( nonatomic) IBOutlet UITextField *inputTextField; 14 | @property ( nonatomic) IBOutlet UIButton *saveButton; 15 | @property ( nonatomic) IBOutlet UIButton *fetchButton; 16 | @property ( nonatomic) IBOutlet UILabel *fetchedValueLabel; 17 | @property ( nonatomic) IBOutlet UILabel *statusLabel; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LockBox/LockBox/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LockBox 4 | // 5 | // Created by Mark H. Granoff on 4/19/12. 6 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Lockbox.h" 11 | 12 | #define kMyKeyString @"MyKeyString" 13 | #define kMyKeyArray @"MyKeyArray" 14 | #define kMyKeySet @"MyKeySet" 15 | #define kMyKeyDictionary @"MyKeyDictionary" 16 | 17 | #define kSaveAsString 0 18 | #define kSaveAsArray 1 19 | #define kSaveAsSet 2 20 | #define kSaveAsDictionary 3 21 | 22 | @interface ViewController () 23 | @property (nonatomic, strong) NSArray *keys; 24 | @end 25 | 26 | @implementation ViewController 27 | 28 | -(void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | 32 | self.keys = @[kMyKeyString, kMyKeyArray, kMyKeySet, kMyKeyDictionary]; 33 | } 34 | 35 | -(IBAction)saveButtonPressed:(id)sender 36 | { 37 | 38 | [_inputTextField resignFirstResponder]; 39 | 40 | UIButton *b = (UIButton *)sender; 41 | NSString *value = _inputTextField.text; 42 | BOOL result = NO; 43 | 44 | NSString *key = _keys[b.tag]; 45 | id object = nil; 46 | 47 | switch (b.tag) { 48 | case kSaveAsString: 49 | object = value; 50 | break; 51 | 52 | case kSaveAsArray: 53 | { 54 | value = [value stringByAppendingString:@" - array element"]; 55 | NSArray *array = [NSArray arrayWithObjects: 56 | [value stringByAppendingString:@" 1"], 57 | [value stringByAppendingString:@" 2"], 58 | [value stringByAppendingString:@" 3"], 59 | nil]; 60 | object = array; 61 | break; 62 | } 63 | 64 | case kSaveAsSet: 65 | { 66 | value = [value stringByAppendingString:@" - set element"]; 67 | NSSet *set = [NSSet setWithObjects: 68 | [value stringByAppendingString:@" 1"], 69 | [value stringByAppendingString:@" 2"], 70 | [value stringByAppendingString:@" 3"], 71 | nil]; 72 | object = set; 73 | break; 74 | } 75 | 76 | case kSaveAsDictionary: 77 | { 78 | value = [value stringByAppendingString:@" - dictionary element"]; 79 | NSDictionary * dictionary = value ? @{@"key 1" : [value stringByAppendingString:@" 1"], 80 | @"key 2" : [value stringByAppendingString:@" 2"], 81 | @"key 3" : [value stringByAppendingString:@" 3"]} : nil; 82 | object = dictionary; 83 | break; 84 | } 85 | 86 | default: 87 | break; 88 | } 89 | 90 | result = [Lockbox archiveObject:object forKey:key]; 91 | 92 | _statusLabel.text = [NSString stringWithFormat:@"%@", 93 | (result ? @"Saved!" : @"Save failed.")]; 94 | 95 | } 96 | 97 | -(IBAction)fetchButtonPressed:(id)sender 98 | { 99 | [_inputTextField resignFirstResponder]; 100 | 101 | UIButton *b = (UIButton *)sender; 102 | NSString *key = _keys[b.tag]; 103 | id object = [Lockbox unarchiveObjectForKey:key]; 104 | 105 | _fetchedValueLabel.text = [object description]; 106 | } 107 | 108 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 109 | { 110 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 111 | } 112 | 113 | 114 | -(BOOL)textFieldShouldReturn:(UITextField *)textField 115 | { 116 | return YES; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /LockBox/LockBox/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LockBox/LockBox/en.lproj/ViewController.xib: -------------------------------------------------------------------------------- 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 | 29 | 30 | 31 | 32 | 39 | 50 | 61 | 72 | 83 | 90 | 101 | 112 | 123 | 134 | 141 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /LockBox/LockBox/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LockBox 4 | // 5 | // Created by Mark H. Granoff on 4/19/12. 6 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LockBox/UnitTests/Lockbox3Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Lockbox3Tests.m 3 | // LockBox 4 | // 5 | // Created by Mark Granoff on 1/27/16. 6 | // Copyright © 2016 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Lockbox.h" 11 | #import 12 | 13 | @interface Lockbox3Tests : XCTestCase 14 | @property (nonatomic, strong) NSString *testString; 15 | @property (nonatomic, strong) NSArray *testArray; 16 | @property (nonatomic, strong) NSSet *testSet; 17 | @property (nonatomic, strong) NSDictionary *testDictionary; 18 | @property (nonatomic, strong) NSDate *testDate; 19 | @end 20 | 21 | @implementation Lockbox3Tests 22 | 23 | - (void)setUp { 24 | [super setUp]; 25 | // Put setup code here. This method is called before the invocation of each test method in the class. 26 | 27 | self.testString = @"TestString"; 28 | self.testArray = @[ @"A", @"B", @"C" ]; 29 | self.testSet = [NSSet setWithArray:self.testArray]; 30 | self.testDictionary = @{@"Key 1": @"A", 31 | @"Key 2": @"B", 32 | @"Key 3": @"C"}; 33 | self.testDate = [NSDate date]; 34 | 35 | } 36 | 37 | - (void)tearDown { 38 | // Put teardown code here. This method is called after the invocation of each test method in the class. 39 | [super tearDown]; 40 | } 41 | 42 | 43 | -(void)testArchiveStringForKey 44 | { 45 | NSString *key = @"TestStringKey"; 46 | XCTAssertTrue([Lockbox archiveObject:_testString forKey:key], @"Should be able to archive a string"); 47 | XCTAssertEqualObjects([Lockbox unarchiveObjectForKey:key], _testString, @"Retrieved string should match original"); 48 | } 49 | 50 | -(void)testDeleteStringForKey 51 | { 52 | NSString *key = @"TestStringKey"; 53 | XCTAssertTrue([Lockbox archiveObject:_testString forKey:key], @"Should be able to store a string"); 54 | XCTAssertEqualObjects([Lockbox unarchiveObjectForKey:key], _testString, @"Retrieved string should match original"); 55 | XCTAssertTrue([Lockbox archiveObject:nil forKey:key], @"Should be able to set string for key to nil"); 56 | XCTAssertNil([Lockbox unarchiveObjectForKey:key], @"Deleted key should return nil"); 57 | } 58 | 59 | -(void)testSetArrayForKey 60 | { 61 | NSString *key = @"TestArrayKey"; 62 | XCTAssertTrue([Lockbox archiveObject:_testArray forKey:key], @"Should be able to store an array"); 63 | NSArray *array = [Lockbox unarchiveObjectForKey:key]; 64 | XCTAssertEqualObjects(array, _testArray, @"Retrieved array should match original"); 65 | 66 | [NSThread sleepForTimeInterval:1.0]; 67 | } 68 | 69 | -(void)testSetSetForKey 70 | { 71 | NSString *key = @"TestSetKey"; 72 | XCTAssertTrue([Lockbox archiveObject:_testSet forKey:key], @"Should be able to store a set"); 73 | NSSet *set = [Lockbox unarchiveObjectForKey:key]; 74 | XCTAssertEqualObjects(set, _testSet, @"Retrieved set should match original"); 75 | 76 | [NSThread sleepForTimeInterval:1.0]; 77 | } 78 | 79 | -(void)testSetDictionaryForKey 80 | { 81 | NSString *key = @"TestDictionaryKey"; 82 | XCTAssertTrue([Lockbox archiveObject:_testDictionary forKey:key], @"Should be able to store a dictionary"); 83 | NSDictionary *dictionary = [Lockbox unarchiveObjectForKey:key]; 84 | XCTAssertEqualObjects(dictionary, _testDictionary, @"Retrieved dictionary should match original"); 85 | 86 | NSString *expectedValueForKey = [_testDictionary objectForKey:@"Key 1"]; 87 | NSString *actualValueForKey = [dictionary objectForKey:@"Key 1"]; 88 | 89 | XCTAssertEqualObjects(expectedValueForKey, actualValueForKey, @"Actual objectForKey value doesn't match expected value"); 90 | 91 | [NSThread sleepForTimeInterval:1.0]; 92 | } 93 | 94 | -(void)testSetSameKeyWithTwoValues 95 | { 96 | XCTAssertTrue([Lockbox archiveObject:@"1" forKey:@"test"], @"Set '1' for key 'test'"); 97 | XCTAssertTrue([[Lockbox unarchiveObjectForKey:@"test"] isEqualToString:@"1"], @"Retrieve '1' for key 'test'"); 98 | XCTAssertTrue([Lockbox archiveObject:@"2" forKey:@"test"], @"Set '2' for key 'test'"); 99 | XCTAssertTrue([[Lockbox unarchiveObjectForKey:@"test"] isEqualToString:@"2"], @"Retrieve '2' for key 'test'"); 100 | } 101 | 102 | -(void)testSetDateForKey 103 | { 104 | NSString *key = @"TestDateKey"; 105 | XCTAssertTrue([Lockbox archiveObject:_testDate forKey:key], @"Should be able to store a date"); 106 | NSDate *date = [Lockbox unarchiveObjectForKey:key]; 107 | XCTAssertEqualObjects(date, _testDate, @"Retrieved date should match original"); 108 | } 109 | 110 | -(void)testSetNoDateForKey 111 | { 112 | NSString *key = @"TestDateKey"; 113 | XCTAssertTrue([Lockbox archiveObject:nil forKey:key], @"Should be able to remove a stored date"); 114 | } 115 | 116 | -(void)testRetrieveDateForNoKey 117 | { 118 | NSString *key =@"NonexistentDateKey"; 119 | XCTAssertNil([Lockbox unarchiveObjectForKey:key], @"Should return nil (date) for nonexistent key"); 120 | } 121 | 122 | -(void)testNilDictionary 123 | { 124 | NSString *key = @"testDict"; 125 | NSDictionary *testDict = @{ @"test1" : @"value1", @"test2" : @"value2" }; 126 | XCTAssertTrue([Lockbox archiveObject:testDict forKey:key], @"Set Dictionary value for key 'testDict'"); 127 | 128 | NSDictionary *savedDict = [Lockbox unarchiveObjectForKey:key]; 129 | XCTAssertTrue(savedDict != nil, @"Retrieved Dictionary for key 'testDict'"); 130 | XCTAssertTrue([savedDict allKeys].count == 2, @"Key count matches stored Dictionary"); 131 | XCTAssertTrue([savedDict[@"test1"] isEqualToString:@"value1"], @"Retrieve Dictionary 'value1' for Dictionary key 'test1'"); 132 | 133 | XCTAssertTrue([Lockbox archiveObject:nil forKey:key], @"Setting Dictionary value to nil for key 'testDict'"); 134 | XCTAssertNil([Lockbox unarchiveObjectForKey:key], @"Confirm Dictionary has been cleared from Lockbox"); 135 | } 136 | 137 | - (void)testNilArray 138 | { 139 | NSString *key = @"testArr"; 140 | NSArray *testArr = @[ @"value1", @"value2", @"value3" ]; 141 | XCTAssertTrue([Lockbox archiveObject:testArr forKey:key], @"Set Array value for key 'testArr'"); 142 | 143 | NSArray *savedArr = [Lockbox unarchiveObjectForKey:key]; 144 | XCTAssertTrue(savedArr != nil, @"Retrieved Array for key 'testArr'"); 145 | XCTAssertTrue(savedArr.count == 3, @"Array count matches stored Array"); 146 | XCTAssertTrue([savedArr[1] isEqualToString:@"value2"], @"Retrieve Array value 'value2' at index 1"); 147 | 148 | XCTAssertTrue([Lockbox archiveObject:nil forKey:key], @"Setting Array value to nil for key 'testArr'"); 149 | XCTAssertNil([Lockbox unarchiveObjectForKey:key], @"Confirm Array has been cleared from Lockbox"); 150 | 151 | } 152 | 153 | - (void)testLocalInstance 154 | { 155 | NSString *customKeyPrefix = @"my.custom.keyPrefix"; 156 | Lockbox *lb = [[Lockbox alloc] initWithKeyPrefix:customKeyPrefix]; 157 | 158 | XCTAssertEqualObjects(customKeyPrefix, lb.keyPrefix, @"Custom key prefix should equal '%@'", customKeyPrefix); 159 | } 160 | 161 | -(void)testArrayOfDictionary 162 | { 163 | NSArray *array = @[ _testDictionary ]; 164 | [Lockbox archiveObject:array forKey:@"testArrayOfDictionary"]; 165 | 166 | NSArray *fetchedArray = [Lockbox unarchiveObjectForKey:@"testArrayOfDictionary"]; 167 | 168 | XCTAssertEqualObjects(array, fetchedArray, @"Fetched array of dictionary should match original"); 169 | 170 | NSDictionary *fetchedDictionary = fetchedArray.firstObject; 171 | 172 | XCTAssertEqualObjects(fetchedDictionary, _testDictionary, @"Fetched dictionary from array should equal _testDictionary"); 173 | } 174 | 175 | -(void)testInvalidArchiveData 176 | { 177 | [Lockbox setString:@"testing" forKey:@"testInvalidArchive"]; 178 | 179 | id data = [Lockbox unarchiveObjectForKey:@"testInvalidArchive"]; 180 | 181 | XCTAssertNil(data, @"Expected nil for an invalid archive"); 182 | } 183 | 184 | -(void)testUserCase 185 | { 186 | if (![[NSUserDefaults standardUserDefaults] stringForKey:@"Initialized"]) { 187 | [Lockbox setString:nil forKey:@"auth_token" accessibility:kSecAttrAccessibleAlways]; 188 | [[NSUserDefaults standardUserDefaults] setValue:@"YES" forKey:@"Initialized"]; 189 | [[NSUserDefaults standardUserDefaults] synchronize]; 190 | } 191 | NSString *authToken = [Lockbox stringForKey:@"auth_token"]; 192 | XCTAssertNil(authToken, @"authToken not nil"); 193 | } 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /LockBox/UnitTests/LockboxTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LockboxTests.m 3 | // LockBox 4 | // 5 | // Created by Mark Granoff on 1/26/13. 6 | // Copyright (c) 2013 Hawk iMedia. All rights reserved. 7 | // 8 | 9 | 10 | #import "Lockbox.h" 11 | #import 12 | 13 | @interface LockboxTests : XCTestCase 14 | @end 15 | 16 | @implementation LockboxTests 17 | { 18 | NSString *testString; 19 | NSArray *testArray; 20 | NSSet *testSet; 21 | NSDictionary *testDictionary; 22 | NSDate *testDate; 23 | } 24 | 25 | -(void)setUp 26 | { 27 | testString = @"TestString"; 28 | testArray = @[ @"A", @"B", @"C" ]; 29 | testSet = [NSSet setWithArray:testArray]; 30 | testDictionary = @{@"Key 1": @"A", 31 | @"Key 2": @"B", 32 | @"Key 3": @"C"}; 33 | testDate = [NSDate date]; 34 | } 35 | 36 | -(void)tearDown 37 | { 38 | 39 | } 40 | 41 | 42 | -(void)testSetStringForKey 43 | { 44 | NSString *key = @"TestStringKey"; 45 | XCTAssertTrue([Lockbox setString:testString forKey:key], @"Should be able to store a string"); 46 | XCTAssertEqualObjects([Lockbox stringForKey:key], testString, @"Retrieved string should match original"); 47 | 48 | [NSThread sleepForTimeInterval:1.0]; 49 | } 50 | 51 | -(void)testDeleteStringForKey 52 | { 53 | NSString *key = @"TestStringKey"; 54 | XCTAssertTrue([Lockbox setString:testString forKey:key], @"Should be able to store a string"); 55 | XCTAssertEqualObjects([Lockbox stringForKey:key], testString, @"Retrieved string should match original"); 56 | XCTAssertTrue([Lockbox setString:nil forKey:key], @"Should be able to set string for key to nil"); 57 | XCTAssertNil([Lockbox stringForKey:key], @"Deleted key should return nil"); 58 | } 59 | 60 | -(void)testSetArrayForKey 61 | { 62 | NSString *key = @"TestArrayKey"; 63 | XCTAssertTrue([Lockbox setArray:testArray forKey:key], @"Should be able to store an array"); 64 | NSArray *array = [Lockbox arrayForKey:key]; 65 | XCTAssertEqualObjects(array, testArray, @"Retrieved array should match original"); 66 | 67 | [NSThread sleepForTimeInterval:1.0]; 68 | } 69 | 70 | -(void)testSetSetForKey 71 | { 72 | NSString *key = @"TestSetKey"; 73 | XCTAssertTrue([Lockbox setSet:testSet forKey:key], @"Should be able to store a set"); 74 | NSSet *set = [Lockbox setForKey:key]; 75 | XCTAssertEqualObjects(set, testSet, @"Retrieved set should match original"); 76 | 77 | [NSThread sleepForTimeInterval:1.0]; 78 | } 79 | 80 | -(void)testSetDictionaryForKey 81 | { 82 | NSString *key = @"TestDictionaryKey"; 83 | XCTAssertTrue([Lockbox setDictionary:testDictionary forKey:key], @"Should be able to store a dictionary"); 84 | NSDictionary *dictionary = [Lockbox dictionaryForKey:key]; 85 | XCTAssertEqualObjects(dictionary, testDictionary, @"Retrieved dictionary should match original"); 86 | 87 | NSString *expectedValueForKey = [testDictionary objectForKey:@"Key 1"]; 88 | NSString *actualValueForKey = [dictionary objectForKey:@"Key 1"]; 89 | 90 | XCTAssertEqualObjects(expectedValueForKey, actualValueForKey, @"Actual objectForKey value doesn't match expected value"); 91 | 92 | [NSThread sleepForTimeInterval:1.0]; 93 | } 94 | 95 | -(void)testSetSameKeyWithTwoValues 96 | { 97 | XCTAssertTrue([Lockbox setString:@"1" forKey:@"test"], @"Set '1' for key 'test'"); 98 | XCTAssertTrue([[Lockbox stringForKey:@"test"] isEqualToString:@"1"], @"Retrieve '1' for key 'test'"); 99 | XCTAssertTrue([Lockbox setString:@"2" forKey:@"test"], @"Set '2' for key 'test'"); 100 | XCTAssertTrue([[Lockbox stringForKey:@"test"] isEqualToString:@"2"], @"Retrieve '2' for key 'test'"); 101 | } 102 | 103 | -(void)testSetDateForKey 104 | { 105 | NSString *key = @"TestDateKey"; 106 | XCTAssertTrue([Lockbox setDate:testDate forKey:key], @"Should be able to store a date"); 107 | NSDate *date = [Lockbox dateForKey:key]; 108 | XCTAssertEqualObjects(date, testDate, @"Retrieved date should match original"); 109 | } 110 | 111 | -(void)testSetNoDateForKey 112 | { 113 | NSString *key = @"TestDateKey"; 114 | XCTAssertTrue([Lockbox setDate:nil forKey:key], @"Should be able to remove a stored date"); 115 | } 116 | 117 | -(void)testRetrieveDateForNoKey 118 | { 119 | NSString *key =@"NonexistentDateKey"; 120 | XCTAssertNil([Lockbox dateForKey:key], @"Should return nil (date) for nonexistent key"); 121 | } 122 | 123 | -(void)testNilDictionary 124 | { 125 | NSString *key = @"testDict"; 126 | NSDictionary *testDict = @{ @"test1" : @"value1", @"test2" : @"value2" }; 127 | XCTAssertTrue([Lockbox setDictionary:testDict forKey:key], @"Set Dictionary value for key 'testDict'"); 128 | 129 | NSDictionary *savedDict = [Lockbox dictionaryForKey:key]; 130 | XCTAssertTrue(savedDict != nil, @"Retrieved Dictionary for key 'testDict'"); 131 | XCTAssertTrue([savedDict allKeys].count == 2, @"Key count matches stored Dictionary"); 132 | XCTAssertTrue([savedDict[@"test1"] isEqualToString:@"value1"], @"Retrieve Dictionary 'value1' for Dictionary key 'test1'"); 133 | 134 | XCTAssertTrue([Lockbox setDictionary:nil forKey:key], @"Setting Dictionary value to nil for key 'testDict'"); 135 | XCTAssertNil([Lockbox dictionaryForKey:key], @"Confirm Dictionary has been cleared from Lockbox"); 136 | } 137 | 138 | - (void)testNilArray 139 | { 140 | NSString *key = @"testArr"; 141 | NSArray *testArr = @[ @"value1", @"value2", @"value3" ]; 142 | XCTAssertTrue([Lockbox setArray:testArr forKey:key], @"Set Array value for key 'testArr'"); 143 | 144 | NSArray *savedArr = [Lockbox arrayForKey:key]; 145 | XCTAssertTrue(savedArr != nil, @"Retrieved Array for key 'testArr'"); 146 | XCTAssertTrue(savedArr.count == 3, @"Array count matches stored Array"); 147 | XCTAssertTrue([savedArr[1] isEqualToString:@"value2"], @"Retrieve Array value 'value2' at index 1"); 148 | 149 | XCTAssertTrue([Lockbox setArray:nil forKey:key], @"Setting Array value to nil for key 'testArr'"); 150 | XCTAssertNil([Lockbox arrayForKey:key], @"Confirm Array has been cleared from Lockbox"); 151 | 152 | } 153 | 154 | - (void)testLocalInstance 155 | { 156 | NSString *customKeyPrefix = @"my.custom.keyPrefix"; 157 | Lockbox *lb = [[Lockbox alloc] initWithKeyPrefix:customKeyPrefix]; 158 | 159 | XCTAssertEqualObjects(customKeyPrefix, lb.keyPrefix, @"Custom key prefix should equal '%@'", customKeyPrefix); 160 | } 161 | 162 | //-(void)testArrayOfDictionary 163 | //{ 164 | // NSArray *array = @[ testDictionary ]; 165 | // [Lockbox setArray:array forKey:@"testArrayOfDictionary"]; 166 | // 167 | // NSArray *fetchedArray = [Lockbox arrayForKey:@"testArrayOfDictionary"]; 168 | // 169 | //// XCTAssertEqualObjects(array, fetchedArray, @"Fetched array of dictionary should match original"); 170 | // 171 | // NSDictionary *fetchedDictionary = fetchedArray.firstObject; 172 | // 173 | // XCTAssertEqualObjects(fetchedDictionary, testDictionary, @"Fetched dictionary from array should equal testDictionary"); 174 | //} 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /LockBox/UnitTests/UnitTests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LockBox/UnitTests/UnitTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'UnitTests' target in the 'UnitTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /LockBox/UnitTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Lockbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // Lockbox.h 3 | // 4 | // Created by Mark H. Granoff on 4/19/12. 5 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @interface Lockbox : NSObject 12 | 13 | #ifdef DEBUG 14 | // For unit tests 15 | @property (strong, nonatomic, readonly) NSString *keyPrefix; 16 | #endif 17 | 18 | @property (assign, nonatomic, readonly) OSStatus lastStatus; 19 | 20 | // When the default key prefix (your app's bundle id) is not sufficient, instantiate your own 21 | // instance of Lockbox specifying your own key prefix, and use the appropriate instance methods 22 | // to store and retrieve keychain data. You can also instantiate your own instance and use the 23 | // default key prefix simply by calling [[Lockbox alloc] init]; 24 | -(instancetype)initWithKeyPrefix:(NSString *)keyPrefix; 25 | 26 | // When adding instance methods, remember to add a corresponding class method. 27 | 28 | -(BOOL)archiveObject:(id)object forKey:(NSString *)key; 29 | -(BOOL)archiveObject:(id)object forKey:(NSString *)key accessibility:(CFTypeRef)accessibility; 30 | 31 | -(id)unarchiveObjectForKey:(NSString *)key; 32 | 33 | -(BOOL)setString:(NSString *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:"); 34 | -(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:accesibility"); 35 | -(NSString *)stringForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -unarchiveObject:forKey:"); 36 | 37 | -(BOOL)setArray:(NSArray *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:"); 38 | -(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to archiveObject:forKey:accesibility"); 39 | -(NSArray *)arrayForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -unarchiveObject:forKey:"); 40 | 41 | -(BOOL)setSet:(NSSet *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:"); 42 | -(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:accesibility"); 43 | -(NSSet *)setForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -unarchiveObject:forKey:"); 44 | 45 | -(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:"); 46 | -(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:accesibility"); 47 | -(NSDictionary *)dictionaryForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -unarchiveObject:forKey:"); 48 | 49 | -(BOOL)setDate:(NSDate *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:"); 50 | -(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to -archiveObject:forKey:accesibility"); 51 | -(NSDate *)dateForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to -unarchiveObject:forKey:"); 52 | 53 | // Class methods that maintain the convenience of storing and retrieving data from the keychain 54 | // using class-level methods. An internal instance of Lockbox is instantiated for the class that 55 | // uses the instance methods above, and a key prefix equal to your app's bundle id. 56 | 57 | +(BOOL)archiveObject:(id)object forKey:(NSString *)key; 58 | +(BOOL)archiveObject:(id)object forKey:(NSString *)key accessibility:(CFTypeRef)accessibility; 59 | 60 | +(id)unarchiveObjectForKey:(NSString *)key; 61 | 62 | +(BOOL)setString:(NSString *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:"); 63 | +(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:accesibility"); 64 | +(NSString *)stringForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +unarchiveObject:forKey:"); 65 | 66 | +(BOOL)setArray:(NSArray *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:"); 67 | +(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:accesibility"); 68 | +(NSArray *)arrayForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +unarchiveObject:forKey:"); 69 | 70 | +(BOOL)setSet:(NSSet *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:"); 71 | +(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:accesibility"); 72 | +(NSSet *)setForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +unarchiveObject:forKey:"); 73 | 74 | +(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:"); 75 | +(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:accesibility"); 76 | +(NSDictionary *)dictionaryForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +unarchiveObject:forKey:"); 77 | 78 | +(BOOL)setDate:(NSDate *)value forKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:"); 79 | +(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility DEPRECATED_MSG_ATTRIBUTE("Migrate to +archiveObject:forKey:accesibility"); 80 | +(NSDate *)dateForKey:(NSString *)key DEPRECATED_MSG_ATTRIBUTE("Migrate to +unarchiveObject:forKey:"); 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Lockbox.m: -------------------------------------------------------------------------------- 1 | // 2 | // Lockbox.m 3 | // 4 | // Created by Mark H. Granoff on 4/19/12. 5 | // Copyright (c) 2012 Hawk iMedia. All rights reserved. 6 | // 7 | 8 | #import "Lockbox.h" 9 | #import 10 | 11 | // Define DLog if user hasn't already defined his own implementation 12 | #ifndef DLog 13 | #ifdef DEBUG 14 | #define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 15 | #else 16 | #define DLog(...) 17 | #endif 18 | #endif 19 | 20 | #define kDelimiter @"-|-" 21 | #define DEFAULT_ACCESSIBILITY kSecAttrAccessibleWhenUnlocked 22 | 23 | #define LOCKBOX_ID __bridge id 24 | #define LOCKBOX_DICTREF __bridge CFDictionaryRef 25 | 26 | static Lockbox *_lockBox = nil; 27 | static NSString *_defaultKeyPrefix = nil; 28 | 29 | @interface Lockbox() 30 | @property (strong, nonatomic, readwrite) NSString *keyPrefix; 31 | @end 32 | 33 | @implementation Lockbox 34 | 35 | +(void)initialize 36 | { 37 | static dispatch_once_t onceToken; 38 | dispatch_once(&onceToken, ^{ 39 | _defaultKeyPrefix = [[[NSBundle bundleForClass:[self class]] infoDictionary] objectForKey:(NSString*)kCFBundleIdentifierKey]; 40 | _lockBox = [[Lockbox alloc] init]; 41 | }); 42 | } 43 | 44 | -(instancetype)init 45 | { 46 | self = [super init]; 47 | if (self) { 48 | self.keyPrefix = _defaultKeyPrefix; 49 | } 50 | return self; 51 | } 52 | 53 | -(instancetype)initWithKeyPrefix:(NSString *)keyPrefix 54 | { 55 | self = [self init]; 56 | if (self) { 57 | if (keyPrefix) 58 | self.keyPrefix = keyPrefix; 59 | } 60 | return self; 61 | } 62 | 63 | -(NSMutableDictionary *)_service 64 | { 65 | NSMutableDictionary* dict = [NSMutableDictionary dictionary]; 66 | 67 | [dict setObject: (LOCKBOX_ID) kSecClassGenericPassword forKey: (LOCKBOX_ID) kSecClass]; 68 | 69 | return dict; 70 | } 71 | 72 | -(NSMutableDictionary *)_query 73 | { 74 | NSMutableDictionary* query = [NSMutableDictionary dictionary]; 75 | 76 | [query setObject: (LOCKBOX_ID) kSecClassGenericPassword forKey: (LOCKBOX_ID) kSecClass]; 77 | [query setObject: (LOCKBOX_ID) kCFBooleanTrue forKey: (LOCKBOX_ID) kSecReturnData]; 78 | 79 | return query; 80 | } 81 | 82 | // Prefix a bare key like "MySecureKey" with the bundle id, so the actual key stored 83 | // is unique to this app, e.g. "com.mycompany.myapp.MySecretKey" 84 | -(NSString *)_hierarchicalKey:(NSString *)key 85 | { 86 | return [_keyPrefix stringByAppendingFormat:@".%@", key]; 87 | } 88 | 89 | -(BOOL)setObject:(NSString *)obj forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 90 | { 91 | NSString *hierKey = [self _hierarchicalKey:key]; 92 | 93 | // If the object is nil, delete the item 94 | if (!obj) { 95 | NSMutableDictionary *query = [self _query]; 96 | [query setObject:hierKey forKey:(LOCKBOX_ID)kSecAttrService]; 97 | _lastStatus = SecItemDelete((LOCKBOX_DICTREF)query); 98 | return (_lastStatus == errSecSuccess); 99 | } 100 | 101 | NSMutableDictionary *dict = [self _service]; 102 | [dict setObject: hierKey forKey: (LOCKBOX_ID) kSecAttrService]; 103 | [dict setObject: (LOCKBOX_ID)(accessibility) forKey: (LOCKBOX_ID) kSecAttrAccessible]; 104 | [dict setObject: [obj dataUsingEncoding:NSUTF8StringEncoding] forKey: (LOCKBOX_ID) kSecValueData]; 105 | 106 | _lastStatus = SecItemAdd ((LOCKBOX_DICTREF) dict, NULL); 107 | if (_lastStatus == errSecDuplicateItem) { 108 | NSMutableDictionary *query = [self _query]; 109 | [query setObject:hierKey forKey:(LOCKBOX_ID)kSecAttrService]; 110 | _lastStatus = SecItemDelete((LOCKBOX_DICTREF)query); 111 | if (_lastStatus == errSecSuccess) 112 | _lastStatus = SecItemAdd((LOCKBOX_DICTREF) dict, NULL); 113 | } 114 | if (_lastStatus != errSecSuccess) 115 | DLog(@"SecItemAdd failed for key %@: %d", hierKey, (int)_lastStatus); 116 | 117 | return (_lastStatus == errSecSuccess); 118 | } 119 | 120 | -(NSString *)objectForKey:(NSString *)key 121 | { 122 | NSString *hierKey = [self _hierarchicalKey:key]; 123 | 124 | NSMutableDictionary *query = [self _query]; 125 | [query setObject:hierKey forKey: (LOCKBOX_ID)kSecAttrService]; 126 | 127 | CFDataRef data = nil; 128 | _lastStatus = 129 | SecItemCopyMatching ( (LOCKBOX_DICTREF) query, (CFTypeRef *) &data ); 130 | if (_lastStatus != errSecSuccess && _lastStatus != errSecItemNotFound) 131 | DLog(@"SecItemCopyMatching failed for key %@: %d", hierKey, (int)_lastStatus); 132 | 133 | if (!data) 134 | return nil; 135 | 136 | NSString *s = [[NSString alloc] initWithData:(__bridge_transfer NSData *)data encoding: NSUTF8StringEncoding]; 137 | 138 | return s; 139 | } 140 | 141 | -(BOOL)setData:(NSData *)obj forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 142 | { 143 | NSString *hierKey = [self _hierarchicalKey:key]; 144 | 145 | // If the object is nil, delete the item 146 | if (!obj) { 147 | NSMutableDictionary *query = [self _query]; 148 | [query setObject:hierKey forKey:(LOCKBOX_ID)kSecAttrService]; 149 | _lastStatus = SecItemDelete((LOCKBOX_DICTREF)query); 150 | return (_lastStatus == errSecSuccess); 151 | } 152 | 153 | NSMutableDictionary *dict = [self _service]; 154 | [dict setObject: hierKey forKey: (LOCKBOX_ID) kSecAttrService]; 155 | [dict setObject: (LOCKBOX_ID)(accessibility) forKey: (LOCKBOX_ID) kSecAttrAccessible]; 156 | [dict setObject: obj forKey: (LOCKBOX_ID) kSecValueData]; 157 | 158 | _lastStatus = SecItemAdd ((LOCKBOX_DICTREF) dict, NULL); 159 | if (_lastStatus == errSecDuplicateItem) { 160 | NSMutableDictionary *query = [self _query]; 161 | [query setObject:hierKey forKey:(LOCKBOX_ID)kSecAttrService]; 162 | _lastStatus = SecItemDelete((LOCKBOX_DICTREF)query); 163 | if (_lastStatus == errSecSuccess) 164 | _lastStatus = SecItemAdd((LOCKBOX_DICTREF) dict, NULL); 165 | } 166 | if (_lastStatus != errSecSuccess) 167 | DLog(@"SecItemAdd failed for key %@: %d", hierKey, (int)_lastStatus); 168 | 169 | return (_lastStatus == errSecSuccess); 170 | } 171 | 172 | -(NSData *)dataForKey:(NSString *)key 173 | { 174 | NSString *hierKey = [self _hierarchicalKey:key]; 175 | 176 | NSMutableDictionary *query = [self _query]; 177 | [query setObject:hierKey forKey: (LOCKBOX_ID)kSecAttrService]; 178 | 179 | CFDataRef data = nil; 180 | _lastStatus = 181 | SecItemCopyMatching ( (LOCKBOX_DICTREF) query, (CFTypeRef *) &data ); 182 | if (_lastStatus != errSecSuccess && _lastStatus != errSecItemNotFound) 183 | DLog(@"SecItemCopyMatching failed for key %@: %d", hierKey, (int)_lastStatus); 184 | 185 | if (!data) 186 | return nil; 187 | 188 | return (__bridge_transfer NSData *)data; 189 | } 190 | 191 | -(BOOL)archiveObject:(id)object forKey:(NSString *)key 192 | { 193 | return [self archiveObject:object forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 194 | } 195 | 196 | -(BOOL)archiveObject:(id)object forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 197 | { 198 | NSMutableData *data = [NSMutableData new]; 199 | NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; 200 | [archiver encodeObject:object forKey:key]; 201 | [archiver finishEncoding]; 202 | 203 | return [self setData:data forKey:key accessibility:accessibility]; 204 | } 205 | 206 | -(id)unarchiveObjectForKey:(NSString *)key 207 | { 208 | NSData *data = [self dataForKey:key]; 209 | if (!data) 210 | return nil; 211 | 212 | id object = nil; 213 | @try { 214 | NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 215 | object = [unarchiver decodeObjectForKey:key]; 216 | } 217 | @catch (NSException *exception) { 218 | DLog(@"failed for key %@: %@", key, exception.description); 219 | } 220 | 221 | return object; 222 | } 223 | 224 | 225 | -(BOOL)setString:(NSString *)value forKey:(NSString *)key 226 | { 227 | return [self setString:value forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 228 | } 229 | 230 | -(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 231 | { 232 | return [self setObject:value forKey:key accessibility:accessibility]; 233 | } 234 | 235 | -(NSString *)stringForKey:(NSString *)key 236 | { 237 | return [self objectForKey:key]; 238 | } 239 | 240 | -(BOOL)setArray:(NSArray *)value forKey:(NSString *)key 241 | { 242 | return [self setArray:value forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 243 | } 244 | 245 | -(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 246 | { 247 | NSString *components = nil; 248 | if (value != nil && value.count > 0) { 249 | components = [value componentsJoinedByString:kDelimiter]; 250 | } 251 | return [self setObject:components forKey:key accessibility:accessibility]; 252 | } 253 | 254 | -(NSArray *)arrayForKey:(NSString *)key 255 | { 256 | NSArray *array = nil; 257 | NSString *components = [self objectForKey:key]; 258 | if (components) 259 | array = [NSArray arrayWithArray:[components componentsSeparatedByString:kDelimiter]]; 260 | 261 | return array; 262 | } 263 | 264 | -(BOOL)setSet:(NSSet *)value forKey:(NSString *)key 265 | { 266 | return [self setSet:value forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 267 | } 268 | 269 | -(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 270 | { 271 | return [self setArray:[value allObjects] forKey:key accessibility:accessibility]; 272 | } 273 | 274 | -(NSSet *)setForKey:(NSString *)key 275 | { 276 | NSSet *set = nil; 277 | NSArray *array = [self arrayForKey:key]; 278 | if (array) 279 | set = [NSSet setWithArray:array]; 280 | 281 | return set; 282 | } 283 | 284 | -(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key 285 | { 286 | return [self setDictionary:value forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 287 | } 288 | 289 | -(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 290 | { 291 | NSMutableArray * keysAndValues = [NSMutableArray arrayWithArray:value.allKeys]; 292 | [keysAndValues addObjectsFromArray:value.allValues]; 293 | 294 | return [self setArray:keysAndValues forKey:key accessibility:accessibility]; 295 | } 296 | 297 | -(NSDictionary *)dictionaryForKey:(NSString *)key 298 | { 299 | NSArray * keysAndValues = [self arrayForKey:key]; 300 | 301 | if (!keysAndValues || keysAndValues.count == 0) 302 | return nil; 303 | 304 | if ((keysAndValues.count % 2) != 0) 305 | { 306 | DLog(@"Dictionary for %@ was not saved properly to keychain", key); 307 | return nil; 308 | } 309 | 310 | NSUInteger half = keysAndValues.count / 2; 311 | NSRange keys = NSMakeRange(0, half); 312 | NSRange values = NSMakeRange(half, half); 313 | return [NSDictionary dictionaryWithObjects:[keysAndValues subarrayWithRange:values] 314 | forKeys:[keysAndValues subarrayWithRange:keys]]; 315 | } 316 | 317 | -(BOOL)setDate:(NSDate *)value forKey:(NSString *)key 318 | { 319 | return [self setDate:value forKey:key accessibility:DEFAULT_ACCESSIBILITY]; 320 | } 321 | 322 | -(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 323 | { 324 | if (!value) 325 | return [self setObject:nil forKey:key accessibility:accessibility]; 326 | NSNumber *rti = [NSNumber numberWithDouble:[value timeIntervalSinceReferenceDate]]; 327 | return [self setObject:[rti stringValue] forKey:key accessibility:accessibility]; 328 | } 329 | 330 | -(NSDate *)dateForKey:(NSString *)key 331 | { 332 | NSString *dateString = [self objectForKey:key]; 333 | if (dateString) 334 | return [NSDate dateWithTimeIntervalSinceReferenceDate:[dateString doubleValue]]; 335 | return nil; 336 | } 337 | 338 | #pragma mark - Class methods 339 | 340 | +(BOOL)archiveObject:(id)object forKey:(NSString *)key 341 | { 342 | return [_lockBox archiveObject:object forKey:key]; 343 | } 344 | 345 | +(BOOL)archiveObject:(id)object forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 346 | { 347 | return [_lockBox archiveObject:object forKey:key accessibility:accessibility]; 348 | } 349 | 350 | +(id)unarchiveObjectForKey:(NSString *)key 351 | { 352 | return [_lockBox unarchiveObjectForKey:key]; 353 | } 354 | 355 | +(BOOL)setString:(NSString *)value forKey:(NSString *)key 356 | { 357 | return [_lockBox setString:value forKey:key]; 358 | } 359 | 360 | +(BOOL)setString:(NSString *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 361 | { 362 | return [_lockBox setString:value forKey:key accessibility:accessibility]; 363 | } 364 | 365 | +(NSString *)stringForKey:(NSString *)key 366 | { 367 | return [_lockBox stringForKey:key]; 368 | } 369 | 370 | +(BOOL)setArray:(NSArray *)value forKey:(NSString *)key 371 | { 372 | return [_lockBox setArray:value forKey:key]; 373 | } 374 | 375 | +(BOOL)setArray:(NSArray *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility; 376 | { 377 | return [_lockBox setArray:value forKey:key accessibility:accessibility]; 378 | } 379 | 380 | +(NSArray *)arrayForKey:(NSString *)key 381 | { 382 | return [_lockBox arrayForKey:key]; 383 | } 384 | 385 | +(BOOL)setSet:(NSSet *)value forKey:(NSString *)key 386 | { 387 | return [_lockBox setSet:value forKey:key]; 388 | } 389 | 390 | +(BOOL)setSet:(NSSet *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 391 | { 392 | return [_lockBox setSet:value forKey:key accessibility:accessibility]; 393 | } 394 | 395 | +(NSSet *)setForKey:(NSString *)key 396 | { 397 | return [_lockBox setForKey:key]; 398 | } 399 | 400 | +(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key 401 | { 402 | return [_lockBox setDictionary:value forKey:key]; 403 | } 404 | 405 | +(BOOL)setDictionary:(NSDictionary *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 406 | { 407 | return [_lockBox setDictionary:value forKey:key accessibility:accessibility]; 408 | } 409 | 410 | +(NSDictionary *)dictionaryForKey:(NSString *)key 411 | { 412 | return [_lockBox dictionaryForKey:key]; 413 | } 414 | 415 | +(BOOL)setDate:(NSDate *)value forKey:(NSString *)key 416 | { 417 | return [_lockBox setDate:value forKey:key]; 418 | } 419 | 420 | +(BOOL)setDate:(NSDate *)value forKey:(NSString *)key accessibility:(CFTypeRef)accessibility 421 | { 422 | return [_lockBox setDate:value forKey:key accessibility:accessibility]; 423 | } 424 | 425 | +(NSDate *)dateForKey:(NSString *)key 426 | { 427 | return [_lockBox dateForKey:key]; 428 | } 429 | 430 | @end 431 | -------------------------------------------------------------------------------- /Lockbox.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Lockbox' 3 | s.version = '3.0.7' 4 | s.license = 'MIT' 5 | s.summary = 'Lockbox is an Objective-C utility class for storing data securely in the keychain. Use it to store small, sensitive bits of data securely.' 6 | s.homepage = 'https://github.com/granoff/Lockbox' 7 | s.author = 'Mark H. Granoff' 8 | 9 | s.source = { :git => 'https://github.com/granoff/Lockbox.git', :tag => s.version } 10 | 11 | s.ios.deployment_target = '11.0' 12 | s.tvos.deployment_target = '11.0' 13 | 14 | s.source_files = 'Lockbox.{h,m}' 15 | s.frameworks = 'Security' 16 | s.requires_arc = true 17 | end 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lockbox 2 | 3 | [![Build Status](https://travis-ci.org/granoff/Lockbox.png)](https://travis-ci.org/granoff/Lockbox) 4 | [![CocoaPods](https://img.shields.io/cocoapods/p/Lockbox.svg)]() 5 | [![CocoaPods](https://img.shields.io/cocoapods/v/Lockbox.svg)]() 6 | [![CocoaPods](https://img.shields.io/cocoapods/l/Lockbox.svg)]() 7 | 8 | Lockbox is an Objective-C utility class for storing data securely in the keychain. Use it to store small, sensitive bits of data securely. 9 | 10 | Looking for a Swift version? Check out [Strongbox](https://github.com/granoff/Strongbox). 11 | 12 | ## Overview 13 | 14 | There are some bits of data that an app sometimes needs to store that are sensitive: 15 | 16 | + Usernames 17 | + Passwords 18 | + In-App Purchase unlocked feature bits 19 | + and anything else that, if in the wrong hands, would be B-A-D. 20 | 21 | The thing to realize is that data stored in `NSUserDefaults` is stored in the clear! For that matter, most everything stored in your app's sandbox is also there in the clear. 22 | 23 | Surprisingly, new and experienced app developers alike often do not realize this, until it's too late. 24 | 25 | The Lockbox class methods make it easy to store and retrieve ~`NSString`s, `NSArray`s, `NSSet`s, `NSDictionary`s, and `NSDate`s~ any Foundation-based object that conforms to `NSSecureCoding` into and from the key chain. You are spared having to deal with the keychain APIs directly! 26 | 27 | For greater security, and to avoid possible collisions between data stored by your app with data stored by other apps (yours or other developers), the keys you provide in the class methods for storing and retrieving data are prefixed with your app's bundle id. The class methods provide some convenience by simplifying the use of Lockbox. But if you need to be able to access a common set of keys between your app, and say, an iOS8 extension, you may need to override the key prefix. For that, you can instantiate your own instance of Lockbox, providing your custom key prefix, and call the same methods (as instance methods) as you would call on the class. (The signatures are the same between class and instance methods. In fact, the class methods operate on a class-static Lockbox instance.) 28 | 29 | The one caveat to keep in mind is that the keychain is really not meant to store large chunks of data, so don't try and store a huge array of data with these APIs simply because you want it secure. In this case, consider alternative encryption techniques. 30 | 31 | ## Methods 32 | 33 | Lockbox 3 includes the following methods, shown here as class methods. The same methods (as instance methods) may be called on your own Lockbox instances. 34 | 35 | ## General object storage and retrieval 36 | 37 | + `+archiveObject:forKey:` 38 | + `+archiveObject:forKey:accessibility:` 39 | + `+unarchiveObject:forKey:` 40 | 41 | These methods use an `NSKeyedArchiver` and `NSKeyedUnarchiver`, respectively, to encode and decode your objects. Your objects must conform toe `NSSecureCoding`. 42 | 43 | The `+archiveObject:forKey:...` methods return `BOOL` indicating if the keychain operation succeeded or failed. The method `+unarchiveObjectForKey:` method returns a non-`nil` value on success or `nil` on failure. The returned value is of type `id`, but you assign this to whatever you know the data type should be. For example: 44 | 45 | ``` 46 | NSDate *today = [NSDate date]; 47 | [Lockbox archiveObject:today forKey:@"theDate"]; 48 | ... 49 | 50 | NSDate *theDate = [Lockbox unarchiveObjectForKey:@"theDate"]; 51 | ``` 52 | 53 | See below for notes on the `accessibility` argument. 54 | 55 | See below for information about migrating from Lockbox v2.x APIs to Lockbox v3 APIs. 56 | 57 | Lockbox 2.x and older include the following deprecated methods, shown here as class methods. The same methods (as instance methods) may be called on your own Lockbox instances. 58 | 59 | ### NSString (deprecated) 60 | 61 | + `+setString:forKey:` 62 | + `+setString:forKey:accessibility:` 63 | + `+stringForKey:` 64 | 65 | ### NSArray (deprecated) 66 | 67 | + `+setArray:forKey:` 68 | + `+setArray:forKey:accessibility:` 69 | + `+arrayForKey:` 70 | 71 | ### NSSet (deprecated) 72 | 73 | + `+setSet:forKey:` 74 | + `+setSet:forKey:accessibility:` 75 | + `+setForKey:` 76 | 77 | ### NSDictionary (deprecated) 78 | 79 | + `+setDictionary:forKey:` 80 | + `+setDictionary:forKey:accessibility:` 81 | + `+dictionaryForKey:` 82 | 83 | ### NSDate (deprecated) 84 | 85 | + `+setDateForKey:` 86 | + `+setDateForKey:accessibility:` 87 | + `+dateForKey:` 88 | 89 | All the `setXxx` methods return `BOOL`, indicating if the keychain operation succeeded or failed. The `xxxForKey` methods return a non-`nil` value on success, or `nil` on failure. 90 | 91 | The `setXxx` methods will overwrite values for keys that already exist in the keychain, or simply add a keychain entry for the key/value pair if it's not already there. 92 | 93 | In all the methods you can use a simple key name, like "MyKey", but know that under the hood Lockbox is prefixing that key with your app's bundle id (if you are using the Class methods, your own key if you are using a Lockbox instance). So the actual key used to store and retrieve the data looks more like "com.mycompany.myapp.MyKey" or "my.custom.key.MyKey". This ensures that your app, and only your app, has access to your data. 94 | 95 | The methods with an `accessibility` argument take a [Keychain Item Accessibility 96 | Constant](http://developer.apple.com/library/ios/#DOCUMENTATION/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898-CH4g-SW318). You can use this to control when your keychain item should be readable. For 97 | example, passing `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` will make 98 | it accessible only while the device is unlocked, and will not migrate this 99 | item to a new device or installation. The methods without a specific 100 | `accessibility` argument will use `kSecAttrAccessibleWhenUnlocked`, the default in recent iOS versions. 101 | 102 | ## Migrating to Lockbox v3 APIs 103 | 104 | There is no automatic migration from the old `setXxx` methods to the new archive/unarchive methods. In fact, that would be nearly impossible as there is no way to know what you stored for any given key to be able to retrieve the data correctly. The v3 APIs are incompatible with the v2.x APIs. 105 | 106 | Manual migration is required. You'll need to fetch your data using the now deprecated type-specific v2.x methods, and then store the data again using the new `archiveObjectForKey:` method. 107 | 108 | You might do this at launch, one time, for all your Lockbox data stored using the old methods. Then make a note by storing another key, either via Lockbox or in User Defaults or somewhere else, that you've done this migration in your app. So on subsequent launches, you wouldn't have to do the migration again. In fact, that would be redundant and probably mess up your data. 109 | 110 | ## ARC Support 111 | 112 | As of v2.0, Lockbox is ARC-only. For non-ARC support, use v1.4.9. 113 | 114 | ## Requirements & Limitations 115 | 116 | To use this class you will need to add the `Security` framework to your project. 117 | 118 | Your project will have to have Keychain Sharing enabled for Lockbox to access the keychain, but you can remove any Keychain Groups that Xcode adds. The entititlement is apparently required for any keychain access, not just sharing. 119 | 120 | This class was written for use under Cocoa Touch and iOS. The code and tests run fine in the iOS simulator under Mac OS. But there are some issues using this class under Cocoa and Mac OS. There are some keychain API differences between the 2 platforms, as it happens. Feel free to fork this repo to make it work for both Cocoa and Cocoa Touch and I'll be happy to consider your pull request! 121 | 122 | ### Note on running unittests on device 123 | If you experience SecItemCopyMatching errors with code 34018 on Lockbox methods while running your app unit tests target on device, your can avoid these by code signing your unit tests .xcttest folder. 124 | 125 | Add Run Script phase to your unit tests target Build Phases with: 126 | 127 | `codesign --verify --force --sign "$CODE_SIGN_IDENTITY" "$CODESIGNING_FOLDER_PATH"` 128 | 129 | 130 | ## Docs 131 | Link to latest CocoaDocs: [cocoadocs.org/docsets/Lockbox/](http://cocoadocs.org/docsets/Lockbox/) 132 | 133 | ## License 134 | 135 | See the LICENSE file for details. 136 | --------------------------------------------------------------------------------