├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Demo ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── KeychainSwiftCBridge.swift └── ViewController.swift ├── Distrib └── KeychainSwiftDistrib.swift ├── ISSUE_TEMPLATE.md ├── KeychainSwift.podspec ├── KeychainSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── ConcatenateSwiftFiles.xcscheme │ ├── Demo.xcscheme │ ├── KeychainSwift-macOS.xcscheme │ ├── KeychainSwift-tvOS.xcscheme │ ├── KeychainSwift-watchOS.xcscheme │ ├── KeychainSwift.xcscheme │ ├── macOS Demo.xcscheme │ └── macOS Tests.xcscheme ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Info.plist ├── KeychainSwift.h ├── KeychainSwift.swift ├── KeychainSwiftAccessOptions.swift ├── PrivacyInfo.xcprivacy └── TegKeychainConstants.swift ├── Tests └── KeychainSwiftTests │ ├── AccessGroupTests.swift │ ├── AllKeysTests.swift │ ├── ClearTests.swift │ ├── ConcurrencyTests.swift │ ├── Info.plist │ ├── KeychainSwiftPrefixedTests.swift │ ├── KeychainSwiftTests-Bridging-Header.h │ ├── KeychainSwiftTests.swift │ ├── SynchronizableTests.swift │ └── macOS Tests │ ├── Info.plist │ └── macOS_Tests.swift ├── graphics ├── keychain-swift-demo-3.png └── keychain_swift_video_tutorial.jpg ├── macOS Demo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist └── ViewController.swift └── scripts └── concatenate_swift_files.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | ./build 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | .DS_Store 20 | Carthage/Checkouts 21 | Carthage/Build 22 | 23 | # Swift package manager 24 | .build/ 25 | Packages/ 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # KeychainSwift version history 2 | 3 | ## 24.0.0 (2024-05-26) 4 | 5 | Added privacy manifest as a resource bundle to CocoaPods Podspec file. 6 | 7 | ## 23.0.0 (2024-05-26) 8 | 9 | Reverted PR #188 (https://github.com/evgenyneu/keychain-swift/pull/188) since it introduced the issue when installing the library with CocoaPods (https://github.com/evgenyneu/keychain-swift/issues/185#issuecomment-2078297077). 10 | 11 | ## 22.0.0 (2024-04-05) 12 | 13 | Added privacy manifest as a resource to CocoaPods Podspec file (https://github.com/evgenyneu/keychain-swift/pull/188). 14 | 15 | ## 21.0.0 (2024-01-20) 16 | 17 | Added privacy manifest (Sources/PrivacyInfo.xcprivacy). 18 | 19 | ## 20.0.0 (2021-01-04) 20 | 21 | Update to Xcode 13. 22 | 23 | ## 19.0.0 (2020-01-04) 24 | 25 | Added `allKeys` property that returns all key names ([lucasmpaim](https://github.com/lucasmpaim)). 26 | 27 | 28 | ## 18.0.0 (2019-11-03) 29 | 30 | Removed deprecated `kSecAttrAccessibleAlways` and `kSecAttrAccessibleAlwaysThisDeviceOnly` access options (https://github.com/evgenyneu/keychain-swift/pull/122). 31 | 32 | 33 | ## 17.0.0 (2019-10-02) 34 | 35 | Added ability to run unit test from Swift Package Manager (https://github.com/evgenyneu/keychain-swift/pull/113). 36 | 37 | 38 | ## 16.0.0 (2019-05-28) 39 | 40 | Made all methods (get, set, delete and clear) thread-safe to prevent crashing when called from different threads. 41 | 42 | 43 | ## 15.0.0 (2019-04-24) 44 | 45 | Added ability to return data as reference ([mediym41](https://github.com/mediym41)). 46 | 47 | 48 | ## 14.0.0 (2019-04-03) 49 | 50 | Update to Swift 5.0 ([schayes04](https://github.com/schayes04)). 51 | 52 | 53 | ## 13.0.0 (2018-10-23) 54 | 55 | Increased watchOS deployment target to 3.0 ([xuaninbox](https://github.com/xuaninbox)). 56 | 57 | 58 | ## 12.0.0 (2018-09-19) 59 | 60 | Update to Swift 4.2 ([beny](https://github.com/beny)). 61 | 62 | 63 | ## 11.0.0 (2018-03-31) 64 | 65 | Added Swift language version to the podspec file for CocoaPods. 66 | 67 | 68 | ## 10.0.0 (2017-10-30) 69 | 70 | Fixed a crash in `getData` when called simultaneously from different threads ([details](https://github.com/evgenyneu/keychain-swift/pull/68)). 71 | 72 | 73 | ## 9.0.0 (2017-09-23) 74 | 75 | Update to Swift 4.0. 76 | 77 | ## 8.0.3 (2017-04-08) 78 | 79 | Fixed Package Manager setup ([maxkramerbcgdv](https://github.com/maxkramerbcgdv)). 80 | 81 | ## 8.0.0 (2017-04-08) 82 | 83 | Added Swif 3.1 support ([CraigSiemens](https://github.com/CraigSiemens)). 84 | 85 | 86 | ## 7.0.0 (2016-10-08) 87 | 88 | Swift package manager support ([diogoguimaraes](https://github.com/diogoguimaraes)). 89 | 90 | 91 | ## 6.0.2 (2016-09-11) 92 | 93 | Change the `public` access modifier to `open` to allow subclassing of the `KeychainSwift` class and overriding its methods ([djensenius](https://github.com/djensenius)). 94 | 95 | 96 | ## 6.0.0 (2016-08-20) 97 | 98 | Update to Xcode 8 beta 6 (thanks, [Tulleb](https://github.com/Tulleb)). 99 | 100 | 101 | ## 5.0.0 (2016-08-13) 102 | 103 | Update to Xcode 8 beta 4/5. 104 | 105 | 106 | ## 3.0.15 (2016-05-19) 107 | 108 | Added the ability to sychronize items on multiple devices (thank you, [mikaoj](https://github.com/mikaoj)). 109 | 110 | 111 | ## 3.0.13 (2016-04-15) 112 | 113 | Added iOS 7 support (https://github.com/marketplacer/keychain-swift/blob/iOS7/Distrib/KeychainSwiftDistrib.swift) 114 | 115 | 116 | ## 3.0.11 (2016-01-24) 117 | 118 | Added methods for setting/getting booleans. 119 | 120 | 121 | ## 3.0.9 (2015-11-09) 122 | 123 | Moved repository to https://github.com/marketplacer/keychain-swift 124 | 125 | 126 | ## 3.0.8 (2015-11-02) 127 | 128 | Added `lastResultCode` property. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidlines 2 | 3 | Thank you for contributing to Keychain Swift library. Feel free to create an issue if you notice a bug or need help. 4 | 5 | 6 | ## Useful things to mention in an issue message 7 | 8 | * Library setup method: file, Carthage, CocoaPods or Swift Package Manager. 9 | * Version of the library, if known. Example: 8.0. 10 | * Xcode version. Example: 8.3.3. 11 | * OS version. Example: iOS 10.3.2. 12 | 13 | ## Submitting a pull request 14 | 15 | Before extending the library please first consider creating an issue to discuss what you are planning to add. Please note that this library is not a full featured wrapper around Keychain API. The purpose of this project is to do few things and do them well. There are many excellent [alternative solutions](https://github.com/evgenyneu/keychain-swift#alternative-solutions) that provide additional features to people who need them. This project, in contrast, is a single-purpose tool that sacrifices functionality in favor of the ease of use. 16 | -------------------------------------------------------------------------------- /Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | 10 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 11 | // Override point for customization after application launch. 12 | return true 13 | } 14 | 15 | func applicationWillResignActive(_ application: UIApplication) { 16 | // 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. 17 | // 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. 18 | } 19 | 20 | func applicationDidEnterBackground(_ application: UIApplication) { 21 | // 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. 22 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 23 | } 24 | 25 | func applicationWillEnterForeground(_ application: UIApplication) { 26 | // 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. 27 | } 28 | 29 | func applicationDidBecomeActive(_ application: UIApplication) { 30 | // 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. 31 | } 32 | 33 | func applicationWillTerminate(_ application: UIApplication) { 34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 35 | } 36 | 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Demo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 40 | 45 | 46 | 47 | 48 | 49 | 50 | 55 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/KeychainSwiftCBridge.swift: -------------------------------------------------------------------------------- 1 | import Security 2 | import Foundation 3 | import KeychainSwift // You might need to remove this import in your project 4 | 5 | /** 6 | 7 | This file can be used in your ObjC project if you want to use KeychainSwift Swift library. 8 | Extend this file to add other functionality for your app. 9 | 10 | How to use 11 | ---------- 12 | 13 | 1. Import swift code in your ObjC file: 14 | 15 | #import "YOUR_PRODUCT_MODULE_NAME-Swift.h" 16 | 17 | 2. Use KeychainSwift in your ObjC code: 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | KeychainSwiftCBridge *keychain = [[KeychainSwiftCBridge alloc] init]; 23 | [keychain set:@"Hello World" forKey:@"my key"]; 24 | NSString *value = [keychain get:@"my key"]; 25 | 26 | 3. You might need to remove `import KeychainSwift` import from this file in your project. 27 | 28 | */ 29 | @objcMembers public class KeychainSwiftCBridge: NSObject { 30 | let keychain = KeychainSwift() 31 | 32 | open var lastResultCode: OSStatus { 33 | get { return keychain.lastResultCode } 34 | } 35 | 36 | open var accessGroup: String? { 37 | set { keychain.accessGroup = newValue } 38 | get { return keychain.accessGroup } 39 | } 40 | 41 | open var synchronizable: Bool { 42 | set { keychain.synchronizable = newValue } 43 | get { return keychain.synchronizable } 44 | } 45 | 46 | 47 | @discardableResult 48 | open func set(_ value: String, forKey key: String) -> Bool { 49 | return keychain.set(value, forKey: key) 50 | } 51 | 52 | @discardableResult 53 | open func setData(_ value: Data, forKey key: String) -> Bool { 54 | return keychain.set(value, forKey: key) 55 | } 56 | 57 | @discardableResult 58 | open func setBool(_ value: Bool, forKey key: String) -> Bool { 59 | return keychain.set(value, forKey: key) 60 | } 61 | 62 | open func get(_ key: String) -> String? { 63 | return keychain.get(key) 64 | } 65 | 66 | open func getData(_ key: String) -> Data? { 67 | return keychain.getData(key) 68 | } 69 | 70 | open func getBool(_ key: String) -> Bool? { 71 | return keychain.getBool(key) 72 | } 73 | 74 | @discardableResult 75 | open func delete(_ key: String) -> Bool { 76 | return keychain.delete(key); 77 | } 78 | 79 | @discardableResult 80 | open func clear() -> Bool { 81 | return keychain.clear() 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import KeychainSwift 3 | 4 | let TegKeychainDemo_keyName = "my key" 5 | 6 | class ViewController: UIViewController { 7 | 8 | @IBOutlet weak var textField: UITextField! 9 | 10 | @IBOutlet weak var valueLabel: UILabel! 11 | 12 | @IBOutlet weak var synchronizableSwitch: UISwitch! 13 | 14 | let keychain = KeychainSwift() 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | updateValueLabel() 20 | } 21 | 22 | @IBAction func onSaveTapped(_ sender: AnyObject) { 23 | closeKeyboard() 24 | 25 | if let text = textField.text { 26 | keychain.synchronizable = synchronizableSwitch.isOn 27 | keychain.set(text, forKey: TegKeychainDemo_keyName) 28 | updateValueLabel() 29 | } 30 | } 31 | 32 | @IBAction func onDeleteTapped(_ sender: AnyObject) { 33 | closeKeyboard() 34 | 35 | keychain.synchronizable = synchronizableSwitch.isOn 36 | keychain.delete(TegKeychainDemo_keyName) 37 | updateValueLabel() 38 | } 39 | 40 | @IBAction func onGetTapped(_ sender: AnyObject) { 41 | closeKeyboard() 42 | 43 | updateValueLabel() 44 | } 45 | 46 | private func updateValueLabel() { 47 | keychain.synchronizable = synchronizableSwitch.isOn 48 | 49 | if let value = keychain.get(TegKeychainDemo_keyName) { 50 | valueLabel.text = "In Keychain: \(value)" 51 | } else { 52 | valueLabel.text = "no value in keychain" 53 | } 54 | } 55 | 56 | private func closeKeyboard() { 57 | textField.resignFirstResponder() 58 | } 59 | 60 | @IBAction func didTapView(_ sender: AnyObject) { 61 | closeKeyboard() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Distrib/KeychainSwiftDistrib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Keychain helper for iOS/Swift. 3 | // 4 | // https://github.com/evgenyneu/keychain-swift 5 | // 6 | // This file was automatically generated by combining multiple Swift source files. 7 | // 8 | 9 | 10 | // ---------------------------- 11 | // 12 | // KeychainSwift.swift 13 | // 14 | // ---------------------------- 15 | 16 | import Security 17 | import Foundation 18 | 19 | /** 20 | 21 | A collection of helper functions for saving text and data in the keychain. 22 | 23 | */ 24 | open class KeychainSwift { 25 | 26 | var lastQueryParameters: [String: Any]? // Used by the unit tests 27 | 28 | /// Contains result code from the last operation. Value is noErr (0) for a successful result. 29 | open var lastResultCode: OSStatus = noErr 30 | 31 | var keyPrefix = "" // Can be useful in test. 32 | 33 | /** 34 | 35 | Specify an access group that will be used to access keychain items. Access groups can be used to share keychain items between applications. When access group value is nil all application access groups are being accessed. Access group name is used by all functions: set, get, delete and clear. 36 | 37 | */ 38 | open var accessGroup: String? 39 | 40 | 41 | /** 42 | 43 | Specifies whether the items can be synchronized with other devices through iCloud. Setting this property to true will 44 | add the item to other devices with the `set` method and obtain synchronizable items with the `get` command. Deleting synchronizable items will remove them from all devices. In order for keychain synchronization to work the user must enable "Keychain" in iCloud settings. 45 | 46 | Does not work on macOS. 47 | 48 | */ 49 | open var synchronizable: Bool = false 50 | 51 | private let lock = NSLock() 52 | 53 | 54 | /// Instantiate a KeychainSwift object 55 | public init() { } 56 | 57 | /** 58 | 59 | - parameter keyPrefix: a prefix that is added before the key in get/set methods. Note that `clear` method still clears everything from the Keychain. 60 | 61 | */ 62 | public init(keyPrefix: String) { 63 | self.keyPrefix = keyPrefix 64 | } 65 | 66 | /** 67 | 68 | Stores the text value in the keychain item under the given key. 69 | 70 | - parameter key: Key under which the text value is stored in the keychain. 71 | - parameter value: Text string to be written to the keychain. 72 | - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 73 | 74 | - returns: True if the text was successfully written to the keychain. 75 | 76 | */ 77 | @discardableResult 78 | open func set(_ value: String, forKey key: String, 79 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 80 | 81 | if let value = value.data(using: String.Encoding.utf8) { 82 | return set(value, forKey: key, withAccess: access) 83 | } 84 | 85 | return false 86 | } 87 | 88 | /** 89 | 90 | Stores the data in the keychain item under the given key. 91 | 92 | - parameter key: Key under which the data is stored in the keychain. 93 | - parameter value: Data to be written to the keychain. 94 | - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 95 | 96 | - returns: True if the text was successfully written to the keychain. 97 | 98 | */ 99 | @discardableResult 100 | open func set(_ value: Data, forKey key: String, 101 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 102 | 103 | // The lock prevents the code to be run simultaneously 104 | // from multiple threads which may result in crashing 105 | lock.lock() 106 | defer { lock.unlock() } 107 | 108 | deleteNoLock(key) // Delete any existing key before saving it 109 | 110 | let accessible = access?.value ?? KeychainSwiftAccessOptions.defaultOption.value 111 | 112 | let prefixedKey = keyWithPrefix(key) 113 | 114 | var query: [String : Any] = [ 115 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 116 | KeychainSwiftConstants.attrAccount : prefixedKey, 117 | KeychainSwiftConstants.valueData : value, 118 | KeychainSwiftConstants.accessible : accessible 119 | ] 120 | 121 | query = addAccessGroupWhenPresent(query) 122 | query = addSynchronizableIfRequired(query, addingItems: true) 123 | lastQueryParameters = query 124 | 125 | lastResultCode = SecItemAdd(query as CFDictionary, nil) 126 | 127 | return lastResultCode == noErr 128 | } 129 | 130 | /** 131 | 132 | Stores the boolean value in the keychain item under the given key. 133 | 134 | - parameter key: Key under which the value is stored in the keychain. 135 | - parameter value: Boolean to be written to the keychain. 136 | - parameter withAccess: Value that indicates when your app needs access to the value in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 137 | 138 | - returns: True if the value was successfully written to the keychain. 139 | 140 | */ 141 | @discardableResult 142 | open func set(_ value: Bool, forKey key: String, 143 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 144 | 145 | let bytes: [UInt8] = value ? [1] : [0] 146 | let data = Data(bytes) 147 | 148 | return set(data, forKey: key, withAccess: access) 149 | } 150 | 151 | /** 152 | 153 | Retrieves the text value from the keychain that corresponds to the given key. 154 | 155 | - parameter key: The key that is used to read the keychain item. 156 | - returns: The text value from the keychain. Returns nil if unable to read the item. 157 | 158 | */ 159 | open func get(_ key: String) -> String? { 160 | if let data = getData(key) { 161 | 162 | if let currentString = String(data: data, encoding: .utf8) { 163 | return currentString 164 | } 165 | 166 | lastResultCode = -67853 // errSecInvalidEncoding 167 | } 168 | 169 | return nil 170 | } 171 | 172 | /** 173 | 174 | Retrieves the data from the keychain that corresponds to the given key. 175 | 176 | - parameter key: The key that is used to read the keychain item. 177 | - parameter asReference: If true, returns the data as reference (needed for things like NEVPNProtocol). 178 | - returns: The text value from the keychain. Returns nil if unable to read the item. 179 | 180 | */ 181 | open func getData(_ key: String, asReference: Bool = false) -> Data? { 182 | // The lock prevents the code to be run simultaneously 183 | // from multiple threads which may result in crashing 184 | lock.lock() 185 | defer { lock.unlock() } 186 | 187 | let prefixedKey = keyWithPrefix(key) 188 | 189 | var query: [String: Any] = [ 190 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 191 | KeychainSwiftConstants.attrAccount : prefixedKey, 192 | KeychainSwiftConstants.matchLimit : kSecMatchLimitOne 193 | ] 194 | 195 | if asReference { 196 | query[KeychainSwiftConstants.returnReference] = kCFBooleanTrue 197 | } else { 198 | query[KeychainSwiftConstants.returnData] = kCFBooleanTrue 199 | } 200 | 201 | query = addAccessGroupWhenPresent(query) 202 | query = addSynchronizableIfRequired(query, addingItems: false) 203 | lastQueryParameters = query 204 | 205 | var result: AnyObject? 206 | 207 | lastResultCode = withUnsafeMutablePointer(to: &result) { 208 | SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) 209 | } 210 | 211 | if lastResultCode == noErr { 212 | return result as? Data 213 | } 214 | 215 | return nil 216 | } 217 | 218 | /** 219 | 220 | Retrieves the boolean value from the keychain that corresponds to the given key. 221 | 222 | - parameter key: The key that is used to read the keychain item. 223 | - returns: The boolean value from the keychain. Returns nil if unable to read the item. 224 | 225 | */ 226 | open func getBool(_ key: String) -> Bool? { 227 | guard let data = getData(key) else { return nil } 228 | guard let firstBit = data.first else { return nil } 229 | return firstBit == 1 230 | } 231 | 232 | /** 233 | 234 | Deletes the single keychain item specified by the key. 235 | 236 | - parameter key: The key that is used to delete the keychain item. 237 | - returns: True if the item was successfully deleted. 238 | 239 | */ 240 | @discardableResult 241 | open func delete(_ key: String) -> Bool { 242 | // The lock prevents the code to be run simultaneously 243 | // from multiple threads which may result in crashing 244 | lock.lock() 245 | defer { lock.unlock() } 246 | 247 | return deleteNoLock(key) 248 | } 249 | 250 | /** 251 | Return all keys from keychain 252 | 253 | - returns: An string array with all keys from the keychain. 254 | 255 | */ 256 | public var allKeys: [String] { 257 | var query: [String: Any] = [ 258 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 259 | KeychainSwiftConstants.returnData : true, 260 | KeychainSwiftConstants.returnAttributes: true, 261 | KeychainSwiftConstants.returnReference: true, 262 | KeychainSwiftConstants.matchLimit: KeychainSwiftConstants.secMatchLimitAll 263 | ] 264 | 265 | query = addAccessGroupWhenPresent(query) 266 | query = addSynchronizableIfRequired(query, addingItems: false) 267 | 268 | var result: AnyObject? 269 | 270 | let lastResultCode = withUnsafeMutablePointer(to: &result) { 271 | SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) 272 | } 273 | 274 | if lastResultCode == noErr { 275 | return (result as? [[String: Any]])?.compactMap { 276 | $0[KeychainSwiftConstants.attrAccount] as? String } ?? [] 277 | } 278 | 279 | return [] 280 | } 281 | 282 | /** 283 | 284 | Same as `delete` but is only accessed internally, since it is not thread safe. 285 | 286 | - parameter key: The key that is used to delete the keychain item. 287 | - returns: True if the item was successfully deleted. 288 | 289 | */ 290 | @discardableResult 291 | func deleteNoLock(_ key: String) -> Bool { 292 | let prefixedKey = keyWithPrefix(key) 293 | 294 | var query: [String: Any] = [ 295 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 296 | KeychainSwiftConstants.attrAccount : prefixedKey 297 | ] 298 | 299 | query = addAccessGroupWhenPresent(query) 300 | query = addSynchronizableIfRequired(query, addingItems: false) 301 | lastQueryParameters = query 302 | 303 | lastResultCode = SecItemDelete(query as CFDictionary) 304 | 305 | return lastResultCode == noErr 306 | } 307 | 308 | /** 309 | 310 | Deletes all Keychain items used by the app. Note that this method deletes all items regardless of the prefix settings used for initializing the class. 311 | 312 | - returns: True if the keychain items were successfully deleted. 313 | 314 | */ 315 | @discardableResult 316 | open func clear() -> Bool { 317 | // The lock prevents the code to be run simultaneously 318 | // from multiple threads which may result in crashing 319 | lock.lock() 320 | defer { lock.unlock() } 321 | 322 | var query: [String: Any] = [ kSecClass as String : kSecClassGenericPassword ] 323 | query = addAccessGroupWhenPresent(query) 324 | query = addSynchronizableIfRequired(query, addingItems: false) 325 | lastQueryParameters = query 326 | 327 | lastResultCode = SecItemDelete(query as CFDictionary) 328 | 329 | return lastResultCode == noErr 330 | } 331 | 332 | /// Returns the key with currently set prefix. 333 | func keyWithPrefix(_ key: String) -> String { 334 | return "\(keyPrefix)\(key)" 335 | } 336 | 337 | func addAccessGroupWhenPresent(_ items: [String: Any]) -> [String: Any] { 338 | guard let accessGroup = accessGroup else { return items } 339 | 340 | var result: [String: Any] = items 341 | result[KeychainSwiftConstants.accessGroup] = accessGroup 342 | return result 343 | } 344 | 345 | /** 346 | 347 | Adds kSecAttrSynchronizable: kSecAttrSynchronizableAny` item to the dictionary when the `synchronizable` property is true. 348 | 349 | - parameter items: The dictionary where the kSecAttrSynchronizable items will be added when requested. 350 | - parameter addingItems: Use `true` when the dictionary will be used with `SecItemAdd` method (adding a keychain item). For getting and deleting items, use `false`. 351 | 352 | - returns: the dictionary with kSecAttrSynchronizable item added if it was requested. Otherwise, it returns the original dictionary. 353 | 354 | */ 355 | func addSynchronizableIfRequired(_ items: [String: Any], addingItems: Bool) -> [String: Any] { 356 | if !synchronizable { return items } 357 | var result: [String: Any] = items 358 | result[KeychainSwiftConstants.attrSynchronizable] = addingItems == true ? true : kSecAttrSynchronizableAny 359 | return result 360 | } 361 | } 362 | 363 | 364 | // ---------------------------- 365 | // 366 | // TegKeychainConstants.swift 367 | // 368 | // ---------------------------- 369 | 370 | import Foundation 371 | import Security 372 | 373 | /// Constants used by the library 374 | public struct KeychainSwiftConstants { 375 | /// Specifies a Keychain access group. Used for sharing Keychain items between apps. 376 | public static var accessGroup: String { return toString(kSecAttrAccessGroup) } 377 | 378 | /** 379 | 380 | A value that indicates when your app needs access to the data in a keychain item. The default value is AccessibleWhenUnlocked. For a list of possible values, see KeychainSwiftAccessOptions. 381 | 382 | */ 383 | public static var accessible: String { return toString(kSecAttrAccessible) } 384 | 385 | /// Used for specifying a String key when setting/getting a Keychain value. 386 | public static var attrAccount: String { return toString(kSecAttrAccount) } 387 | 388 | /// Used for specifying synchronization of keychain items between devices. 389 | public static var attrSynchronizable: String { return toString(kSecAttrSynchronizable) } 390 | 391 | /// An item class key used to construct a Keychain search dictionary. 392 | public static var klass: String { return toString(kSecClass) } 393 | 394 | /// Specifies the number of values returned from the keychain. The library only supports single values. 395 | public static var matchLimit: String { return toString(kSecMatchLimit) } 396 | 397 | /// A return data type used to get the data from the Keychain. 398 | public static var returnData: String { return toString(kSecReturnData) } 399 | 400 | /// Used for specifying a value when setting a Keychain value. 401 | public static var valueData: String { return toString(kSecValueData) } 402 | 403 | /// Used for returning a reference to the data from the keychain 404 | public static var returnReference: String { return toString(kSecReturnPersistentRef) } 405 | 406 | /// A key whose value is a Boolean indicating whether or not to return item attributes 407 | public static var returnAttributes : String { return toString(kSecReturnAttributes) } 408 | 409 | /// A value that corresponds to matching an unlimited number of items 410 | public static var secMatchLimitAll : String { return toString(kSecMatchLimitAll) } 411 | 412 | static func toString(_ value: CFString) -> String { 413 | return value as String 414 | } 415 | } 416 | 417 | 418 | // ---------------------------- 419 | // 420 | // KeychainSwiftAccessOptions.swift 421 | // 422 | // ---------------------------- 423 | 424 | import Security 425 | 426 | /** 427 | 428 | These options are used to determine when a keychain item should be readable. The default value is AccessibleWhenUnlocked. 429 | 430 | */ 431 | public enum KeychainSwiftAccessOptions { 432 | 433 | /** 434 | 435 | The data in the keychain item can be accessed only while the device is unlocked by the user. 436 | 437 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups. 438 | 439 | This is the default value for keychain items added without explicitly setting an accessibility constant. 440 | 441 | */ 442 | case accessibleWhenUnlocked 443 | 444 | /** 445 | 446 | The data in the keychain item can be accessed only while the device is unlocked by the user. 447 | 448 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. 449 | 450 | */ 451 | case accessibleWhenUnlockedThisDeviceOnly 452 | 453 | /** 454 | 455 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. 456 | 457 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups. 458 | 459 | */ 460 | case accessibleAfterFirstUnlock 461 | 462 | /** 463 | 464 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. 465 | 466 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. 467 | 468 | */ 469 | case accessibleAfterFirstUnlockThisDeviceOnly 470 | 471 | /** 472 | 473 | The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device. 474 | 475 | This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted. 476 | 477 | */ 478 | case accessibleWhenPasscodeSetThisDeviceOnly 479 | 480 | static var defaultOption: KeychainSwiftAccessOptions { 481 | return .accessibleWhenUnlocked 482 | } 483 | 484 | var value: String { 485 | switch self { 486 | case .accessibleWhenUnlocked: 487 | return toString(kSecAttrAccessibleWhenUnlocked) 488 | 489 | case .accessibleWhenUnlockedThisDeviceOnly: 490 | return toString(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) 491 | 492 | case .accessibleAfterFirstUnlock: 493 | return toString(kSecAttrAccessibleAfterFirstUnlock) 494 | 495 | case .accessibleAfterFirstUnlockThisDeviceOnly: 496 | return toString(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) 497 | 498 | case .accessibleWhenPasscodeSetThisDeviceOnly: 499 | return toString(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) 500 | } 501 | } 502 | 503 | func toString(_ value: CFString) -> String { 504 | return KeychainSwiftConstants.toString(value) 505 | } 506 | } 507 | 508 | 509 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please consider submitting the following information (if relevant): 2 | 3 | * Library setup method: file, Carthage, CocoaPods or Swift Package Manager. 4 | * Version of the library. Example: 8.0. 5 | * Xcode version. Example: 8.3.3. 6 | * OS version. Example: iOS 10.3.2. -------------------------------------------------------------------------------- /KeychainSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "KeychainSwift" 3 | s.version = "24.0.0" 4 | s.license = { :type => "MIT" } 5 | s.homepage = "https://github.com/evgenyneu/keychain-swift" 6 | s.summary = "A library for saving text and data in the Keychain with Swift." 7 | s.description = <<-DESC 8 | This is a collection of helper functions for saving text and data in the Keychain. 9 | 10 | * Write and read text and Data with simple functions. 11 | * Specify optional access rule for the keychain item. 12 | * Limit operations to a specific access group. 13 | DESC 14 | s.authors = { "Evgenii Neumerzhitckii" => "sausageskin@gmail.com" } 15 | s.source = { :git => "https://github.com/evgenyneu/keychain-swift.git", :tag => s.version } 16 | s.screenshots = "https://raw.githubusercontent.com/evgenyneu/keychain-swift/master/graphics/keychain-swift-demo-3.png" 17 | s.source_files = "Sources/*.swift" 18 | s.resource_bundles = {"KeychainSwift" => ["Sources/PrivacyInfo.xcprivacy"]} 19 | s.ios.deployment_target = "12.0" 20 | s.osx.deployment_target = "10.13" 21 | s.watchos.deployment_target = "4.0" 22 | s.tvos.deployment_target = "12.0" 23 | s.swift_version = "5.0" 24 | end 25 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 232B4C821BC2995D001F2B7A /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 11 | 232B4C831BC2995D001F2B7A /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 12 | 232B4C841BC2995D001F2B7A /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; 13 | 232B4C921BC29991001F2B7A /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 14 | 232B4C931BC29991001F2B7A /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 15 | 232B4C941BC29991001F2B7A /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; 16 | 23E785681BDA415000B7564A /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 17 | 23E785691BDA415000B7564A /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 18 | 23E7856A1BDA415000B7564A /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; 19 | 5085669B1FA34EB1004208ED /* AccessGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566911FA34EB1004208ED /* AccessGroupTests.swift */; }; 20 | 5085669C1FA34EB1004208ED /* AccessGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566911FA34EB1004208ED /* AccessGroupTests.swift */; }; 21 | 5085669D1FA34EB1004208ED /* ClearTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566921FA34EB1004208ED /* ClearTests.swift */; }; 22 | 508566A11FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566941FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift */; }; 23 | 508566A21FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566941FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift */; }; 24 | 508566A31FA34EB1004208ED /* KeychainSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566961FA34EB1004208ED /* KeychainSwiftTests.swift */; }; 25 | 508566A41FA34EB1004208ED /* KeychainSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566961FA34EB1004208ED /* KeychainSwiftTests.swift */; }; 26 | 508566A71FA34EB1004208ED /* macOS_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566991FA34EB1004208ED /* macOS_Tests.swift */; }; 27 | 508566A81FA34EB1004208ED /* macOS_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508566991FA34EB1004208ED /* macOS_Tests.swift */; }; 28 | 508566A91FA34EB1004208ED /* SynchronizableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */; }; 29 | 508566AA1FA34EB1004208ED /* SynchronizableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */; }; 30 | 7E3A6B7E1D3F6779007C5B1F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E3A6B7D1D3F6779007C5B1F /* AppDelegate.swift */; }; 31 | 7E3A6B801D3F6779007C5B1F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E3A6B7F1D3F6779007C5B1F /* ViewController.swift */; }; 32 | 7E3A6B821D3F6779007C5B1F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7E3A6B811D3F6779007C5B1F /* Assets.xcassets */; }; 33 | 7E3A6B851D3F6779007C5B1F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E3A6B831D3F6779007C5B1F /* Main.storyboard */; }; 34 | 7E3A6B8A1D3F67B1007C5B1F /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 232B4C7A1BC29950001F2B7A /* KeychainSwift.framework */; }; 35 | 7E3A6B8B1D3F67B1007C5B1F /* KeychainSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 232B4C7A1BC29950001F2B7A /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36 | 7ED6C9721B1C118F00FE8090 /* KeychainSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED6C9711B1C118F00FE8090 /* KeychainSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 37 | 7ED6C9781B1C118F00FE8090 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */; }; 38 | 7ED6C9901B1C128100FE8090 /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 39 | 7ED6C9911B1C128100FE8090 /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 40 | 7ED6C9921B1C128100FE8090 /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; 41 | 7ED6C9961B1C12B100FE8090 /* KeychainSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */; }; 42 | 7ED6C9971B1C12B300FE8090 /* KeychainSwiftAccessOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */; }; 43 | 7ED6C9981B1C12B500FE8090 /* TegKeychainConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */; }; 44 | 7ED6C9A21B1C133500FE8090 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C9A11B1C133500FE8090 /* AppDelegate.swift */; }; 45 | 7ED6C9A41B1C133500FE8090 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED6C9A31B1C133500FE8090 /* ViewController.swift */; }; 46 | 7ED6C9A71B1C133500FE8090 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7ED6C9A51B1C133500FE8090 /* Main.storyboard */; }; 47 | 7ED6C9A91B1C133500FE8090 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7ED6C9A81B1C133500FE8090 /* Images.xcassets */; }; 48 | 7ED6C9AC1B1C133500FE8090 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ED6C9AA1B1C133500FE8090 /* LaunchScreen.xib */; }; 49 | 7ED6C9BF1B1C13AA00FE8090 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */; }; 50 | 7ED6C9C01B1C13AA00FE8090 /* KeychainSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51 | 7EE5B9A11E32F6D400AA56FF /* KeychainSwiftCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EE5B9A01E32F6D400AA56FF /* KeychainSwiftCBridge.swift */; }; 52 | C7E1DE4C1E4B7C9F003818F6 /* ConcurrencyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E1DE4A1E4B7C9F003818F6 /* ConcurrencyTests.swift */; }; 53 | F271A1AE23BE071800FCC3B9 /* AllKeysTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F271A1AD23BE071800FCC3B9 /* AllKeysTests.swift */; }; 54 | F271A1AF23BE082500FCC3B9 /* AllKeysTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F271A1AD23BE071800FCC3B9 /* AllKeysTests.swift */; }; 55 | /* End PBXBuildFile section */ 56 | 57 | /* Begin PBXContainerItemProxy section */ 58 | 7E3A6B8C1D3F67B1007C5B1F /* PBXContainerItemProxy */ = { 59 | isa = PBXContainerItemProxy; 60 | containerPortal = 7ED6C9631B1C118F00FE8090 /* Project object */; 61 | proxyType = 1; 62 | remoteGlobalIDString = 232B4C791BC29950001F2B7A; 63 | remoteInfo = "KeychainSwift-macOS"; 64 | }; 65 | 7EC243561E3301D600C771BE /* PBXContainerItemProxy */ = { 66 | isa = PBXContainerItemProxy; 67 | containerPortal = 7ED6C9631B1C118F00FE8090 /* Project object */; 68 | proxyType = 1; 69 | remoteGlobalIDString = 7ED6C99C1B1C133500FE8090; 70 | remoteInfo = Demo; 71 | }; 72 | 7ED6C9791B1C118F00FE8090 /* PBXContainerItemProxy */ = { 73 | isa = PBXContainerItemProxy; 74 | containerPortal = 7ED6C9631B1C118F00FE8090 /* Project object */; 75 | proxyType = 1; 76 | remoteGlobalIDString = 7ED6C96B1B1C118F00FE8090; 77 | remoteInfo = KeychainSwift; 78 | }; 79 | 7ED6C9C11B1C13AA00FE8090 /* PBXContainerItemProxy */ = { 80 | isa = PBXContainerItemProxy; 81 | containerPortal = 7ED6C9631B1C118F00FE8090 /* Project object */; 82 | proxyType = 1; 83 | remoteGlobalIDString = 7ED6C96B1B1C118F00FE8090; 84 | remoteInfo = KeychainSwift; 85 | }; 86 | BF4250102343FE8400F7B44C /* PBXContainerItemProxy */ = { 87 | isa = PBXContainerItemProxy; 88 | containerPortal = 7ED6C9631B1C118F00FE8090 /* Project object */; 89 | proxyType = 1; 90 | remoteGlobalIDString = 232B4C791BC29950001F2B7A; 91 | remoteInfo = "KeychainSwift-macOS"; 92 | }; 93 | /* End PBXContainerItemProxy section */ 94 | 95 | /* Begin PBXCopyFilesBuildPhase section */ 96 | 7E3A6B8E1D3F67B1007C5B1F /* Embed Frameworks */ = { 97 | isa = PBXCopyFilesBuildPhase; 98 | buildActionMask = 2147483647; 99 | dstPath = ""; 100 | dstSubfolderSpec = 10; 101 | files = ( 102 | 7E3A6B8B1D3F67B1007C5B1F /* KeychainSwift.framework in Embed Frameworks */, 103 | ); 104 | name = "Embed Frameworks"; 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 7ED6C9C31B1C13AA00FE8090 /* Embed Frameworks */ = { 108 | isa = PBXCopyFilesBuildPhase; 109 | buildActionMask = 2147483647; 110 | dstPath = ""; 111 | dstSubfolderSpec = 10; 112 | files = ( 113 | 7ED6C9C01B1C13AA00FE8090 /* KeychainSwift.framework in Embed Frameworks */, 114 | ); 115 | name = "Embed Frameworks"; 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXCopyFilesBuildPhase section */ 119 | 120 | /* Begin PBXFileReference section */ 121 | 232B4C7A1BC29950001F2B7A /* KeychainSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 122 | 232B4C8A1BC29984001F2B7A /* KeychainSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 123 | 23E785601BDA410C00B7564A /* KeychainSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 124 | 508566911FA34EB1004208ED /* AccessGroupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessGroupTests.swift; sourceTree = ""; }; 125 | 508566921FA34EB1004208ED /* ClearTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClearTests.swift; sourceTree = ""; }; 126 | 508566931FA34EB1004208ED /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 127 | 508566941FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftPrefixedTests.swift; sourceTree = ""; }; 128 | 508566951FA34EB1004208ED /* KeychainSwiftTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KeychainSwiftTests-Bridging-Header.h"; sourceTree = ""; }; 129 | 508566961FA34EB1004208ED /* KeychainSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftTests.swift; sourceTree = ""; }; 130 | 508566981FA34EB1004208ED /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 131 | 508566991FA34EB1004208ED /* macOS_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = macOS_Tests.swift; sourceTree = ""; }; 132 | 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizableTests.swift; sourceTree = ""; }; 133 | 7E3A6B601D3F62C2007C5B1F /* macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 134 | 7E3A6B7B1D3F6779007C5B1F /* macOS Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "macOS Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 135 | 7E3A6B7D1D3F6779007C5B1F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 136 | 7E3A6B7F1D3F6779007C5B1F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 137 | 7E3A6B811D3F6779007C5B1F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 138 | 7E3A6B841D3F6779007C5B1F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 139 | 7E3A6B861D3F6779007C5B1F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140 | 7E6EC4402B4900CC0064B6B1 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 141 | 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 142 | 7ED6C9701B1C118F00FE8090 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 143 | 7ED6C9711B1C118F00FE8090 /* KeychainSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeychainSwift.h; sourceTree = ""; }; 144 | 7ED6C9771B1C118F00FE8090 /* KeychainSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 145 | 7ED6C9881B1C125000FE8090 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 146 | 7ED6C98B1B1C126D00FE8090 /* keychain-swift-demo-3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "keychain-swift-demo-3.png"; sourceTree = ""; }; 147 | 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwift.swift; sourceTree = ""; }; 148 | 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftAccessOptions.swift; sourceTree = ""; }; 149 | 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TegKeychainConstants.swift; sourceTree = ""; }; 150 | 7ED6C99D1B1C133500FE8090 /* Keychain Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Keychain Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 151 | 7ED6C9A01B1C133500FE8090 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 152 | 7ED6C9A11B1C133500FE8090 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 153 | 7ED6C9A31B1C133500FE8090 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 154 | 7ED6C9A61B1C133500FE8090 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 155 | 7ED6C9A81B1C133500FE8090 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 156 | 7ED6C9AB1B1C133500FE8090 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 157 | 7ED6C9C61B1C16BC00FE8090 /* concatenate_swift_files.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = concatenate_swift_files.sh; sourceTree = ""; }; 158 | 7ED6C9C91B1C16EE00FE8090 /* KeychainSwiftDistrib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftDistrib.swift; sourceTree = ""; }; 159 | 7EE5B9A01E32F6D400AA56FF /* KeychainSwiftCBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftCBridge.swift; sourceTree = ""; }; 160 | C7E1DE4A1E4B7C9F003818F6 /* ConcurrencyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrencyTests.swift; sourceTree = ""; }; 161 | F271A1AD23BE071800FCC3B9 /* AllKeysTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllKeysTests.swift; sourceTree = ""; }; 162 | /* End PBXFileReference section */ 163 | 164 | /* Begin PBXFrameworksBuildPhase section */ 165 | 232B4C761BC29950001F2B7A /* Frameworks */ = { 166 | isa = PBXFrameworksBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | 232B4C861BC29984001F2B7A /* Frameworks */ = { 173 | isa = PBXFrameworksBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | 23E7855C1BDA410C00B7564A /* Frameworks */ = { 180 | isa = PBXFrameworksBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | 7E3A6B5D1D3F62C2007C5B1F /* Frameworks */ = { 187 | isa = PBXFrameworksBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | 7E3A6B781D3F6779007C5B1F /* Frameworks */ = { 194 | isa = PBXFrameworksBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 7E3A6B8A1D3F67B1007C5B1F /* KeychainSwift.framework in Frameworks */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | 7ED6C9681B1C118F00FE8090 /* Frameworks */ = { 202 | isa = PBXFrameworksBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | 7ED6C9741B1C118F00FE8090 /* Frameworks */ = { 209 | isa = PBXFrameworksBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | 7ED6C9781B1C118F00FE8090 /* KeychainSwift.framework in Frameworks */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | 7ED6C99A1B1C133500FE8090 /* Frameworks */ = { 217 | isa = PBXFrameworksBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 7ED6C9BF1B1C13AA00FE8090 /* KeychainSwift.framework in Frameworks */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXFrameworksBuildPhase section */ 225 | 226 | /* Begin PBXGroup section */ 227 | 508566901FA34EB1004208ED /* KeychainSwiftTests */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 508566911FA34EB1004208ED /* AccessGroupTests.swift */, 231 | 508566921FA34EB1004208ED /* ClearTests.swift */, 232 | C7E1DE4A1E4B7C9F003818F6 /* ConcurrencyTests.swift */, 233 | 508566931FA34EB1004208ED /* Info.plist */, 234 | 508566941FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift */, 235 | 508566951FA34EB1004208ED /* KeychainSwiftTests-Bridging-Header.h */, 236 | 508566961FA34EB1004208ED /* KeychainSwiftTests.swift */, 237 | 508566971FA34EB1004208ED /* macOS Tests */, 238 | 5085669A1FA34EB1004208ED /* SynchronizableTests.swift */, 239 | F271A1AD23BE071800FCC3B9 /* AllKeysTests.swift */, 240 | ); 241 | path = KeychainSwiftTests; 242 | sourceTree = ""; 243 | }; 244 | 508566971FA34EB1004208ED /* macOS Tests */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | 508566981FA34EB1004208ED /* Info.plist */, 248 | 508566991FA34EB1004208ED /* macOS_Tests.swift */, 249 | ); 250 | path = "macOS Tests"; 251 | sourceTree = ""; 252 | }; 253 | 7E3A6B7C1D3F6779007C5B1F /* macOS Demo */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | 7E3A6B7D1D3F6779007C5B1F /* AppDelegate.swift */, 257 | 7E3A6B7F1D3F6779007C5B1F /* ViewController.swift */, 258 | 7E3A6B811D3F6779007C5B1F /* Assets.xcassets */, 259 | 7E3A6B831D3F6779007C5B1F /* Main.storyboard */, 260 | 7E3A6B861D3F6779007C5B1F /* Info.plist */, 261 | ); 262 | path = "macOS Demo"; 263 | sourceTree = ""; 264 | }; 265 | 7ED6C9621B1C118F00FE8090 = { 266 | isa = PBXGroup; 267 | children = ( 268 | 7ED6C96E1B1C118F00FE8090 /* Sources */, 269 | 7ED6C97B1B1C118F00FE8090 /* Tests */, 270 | 7ED6C99E1B1C133500FE8090 /* Demo */, 271 | 7ED6C98A1B1C126D00FE8090 /* graphics */, 272 | 7ED6C9C81B1C16EE00FE8090 /* Distrib */, 273 | 7ED6C9C51B1C16BC00FE8090 /* scripts */, 274 | 7E3A6B7C1D3F6779007C5B1F /* macOS Demo */, 275 | 7ED6C96D1B1C118F00FE8090 /* Products */, 276 | 7ED6C9881B1C125000FE8090 /* README.md */, 277 | ); 278 | sourceTree = ""; 279 | }; 280 | 7ED6C96D1B1C118F00FE8090 /* Products */ = { 281 | isa = PBXGroup; 282 | children = ( 283 | 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */, 284 | 7ED6C9771B1C118F00FE8090 /* KeychainSwiftTests.xctest */, 285 | 7ED6C99D1B1C133500FE8090 /* Keychain Swift.app */, 286 | 232B4C7A1BC29950001F2B7A /* KeychainSwift.framework */, 287 | 232B4C8A1BC29984001F2B7A /* KeychainSwift.framework */, 288 | 23E785601BDA410C00B7564A /* KeychainSwift.framework */, 289 | 7E3A6B601D3F62C2007C5B1F /* macOS Tests.xctest */, 290 | 7E3A6B7B1D3F6779007C5B1F /* macOS Demo.app */, 291 | ); 292 | name = Products; 293 | sourceTree = ""; 294 | }; 295 | 7ED6C96E1B1C118F00FE8090 /* Sources */ = { 296 | isa = PBXGroup; 297 | children = ( 298 | 7ED6C98D1B1C128100FE8090 /* KeychainSwift.swift */, 299 | 7ED6C98E1B1C128100FE8090 /* KeychainSwiftAccessOptions.swift */, 300 | 7ED6C98F1B1C128100FE8090 /* TegKeychainConstants.swift */, 301 | 7ED6C9711B1C118F00FE8090 /* KeychainSwift.h */, 302 | 7ED6C96F1B1C118F00FE8090 /* Supporting Files */, 303 | 7E6EC4402B4900CC0064B6B1 /* PrivacyInfo.xcprivacy */, 304 | ); 305 | path = Sources; 306 | sourceTree = ""; 307 | }; 308 | 7ED6C96F1B1C118F00FE8090 /* Supporting Files */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | 7ED6C9701B1C118F00FE8090 /* Info.plist */, 312 | ); 313 | name = "Supporting Files"; 314 | sourceTree = ""; 315 | }; 316 | 7ED6C97B1B1C118F00FE8090 /* Tests */ = { 317 | isa = PBXGroup; 318 | children = ( 319 | 508566901FA34EB1004208ED /* KeychainSwiftTests */, 320 | ); 321 | path = Tests; 322 | sourceTree = ""; 323 | }; 324 | 7ED6C98A1B1C126D00FE8090 /* graphics */ = { 325 | isa = PBXGroup; 326 | children = ( 327 | 7ED6C98B1B1C126D00FE8090 /* keychain-swift-demo-3.png */, 328 | ); 329 | path = graphics; 330 | sourceTree = ""; 331 | }; 332 | 7ED6C99E1B1C133500FE8090 /* Demo */ = { 333 | isa = PBXGroup; 334 | children = ( 335 | 7ED6C9A11B1C133500FE8090 /* AppDelegate.swift */, 336 | 7ED6C9A31B1C133500FE8090 /* ViewController.swift */, 337 | 7EE5B9A01E32F6D400AA56FF /* KeychainSwiftCBridge.swift */, 338 | 7ED6C9A51B1C133500FE8090 /* Main.storyboard */, 339 | 7ED6C9A81B1C133500FE8090 /* Images.xcassets */, 340 | 7ED6C9AA1B1C133500FE8090 /* LaunchScreen.xib */, 341 | 7ED6C99F1B1C133500FE8090 /* Supporting Files */, 342 | ); 343 | path = Demo; 344 | sourceTree = ""; 345 | }; 346 | 7ED6C99F1B1C133500FE8090 /* Supporting Files */ = { 347 | isa = PBXGroup; 348 | children = ( 349 | 7ED6C9A01B1C133500FE8090 /* Info.plist */, 350 | ); 351 | name = "Supporting Files"; 352 | sourceTree = ""; 353 | }; 354 | 7ED6C9C51B1C16BC00FE8090 /* scripts */ = { 355 | isa = PBXGroup; 356 | children = ( 357 | 7ED6C9C61B1C16BC00FE8090 /* concatenate_swift_files.sh */, 358 | ); 359 | path = scripts; 360 | sourceTree = ""; 361 | }; 362 | 7ED6C9C81B1C16EE00FE8090 /* Distrib */ = { 363 | isa = PBXGroup; 364 | children = ( 365 | 7ED6C9C91B1C16EE00FE8090 /* KeychainSwiftDistrib.swift */, 366 | ); 367 | path = Distrib; 368 | sourceTree = ""; 369 | }; 370 | /* End PBXGroup section */ 371 | 372 | /* Begin PBXHeadersBuildPhase section */ 373 | 232B4C771BC29950001F2B7A /* Headers */ = { 374 | isa = PBXHeadersBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | 232B4C871BC29984001F2B7A /* Headers */ = { 381 | isa = PBXHeadersBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | 23E7855D1BDA410C00B7564A /* Headers */ = { 388 | isa = PBXHeadersBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | 7ED6C9691B1C118F00FE8090 /* Headers */ = { 395 | isa = PBXHeadersBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | 7ED6C9721B1C118F00FE8090 /* KeychainSwift.h in Headers */, 399 | ); 400 | runOnlyForDeploymentPostprocessing = 0; 401 | }; 402 | /* End PBXHeadersBuildPhase section */ 403 | 404 | /* Begin PBXLegacyTarget section */ 405 | 7ED51A751B7734AF005B39C2 /* ConcatenateSwiftFiles */ = { 406 | isa = PBXLegacyTarget; 407 | buildArgumentsString = "$PROJECT_DIR/Sources $PROJECT_DIR/Distrib/KeychainSwiftDistrib.swift \"//\n// Keychain helper for iOS/Swift.\n//\n// https://github.com/evgenyneu/keychain-swift\n//\n// This file was automatically generated by combining multiple Swift source files.\n//\""; 408 | buildConfigurationList = 7ED51A781B7734AF005B39C2 /* Build configuration list for PBXLegacyTarget "ConcatenateSwiftFiles" */; 409 | buildPhases = ( 410 | ); 411 | buildToolPath = $PROJECT_DIR/scripts/concatenate_swift_files.sh; 412 | buildWorkingDirectory = ""; 413 | dependencies = ( 414 | ); 415 | name = ConcatenateSwiftFiles; 416 | passBuildSettingsInEnvironment = 1; 417 | productName = ConcatenateSwiftFiles; 418 | }; 419 | /* End PBXLegacyTarget section */ 420 | 421 | /* Begin PBXNativeTarget section */ 422 | 232B4C791BC29950001F2B7A /* KeychainSwift-macOS */ = { 423 | isa = PBXNativeTarget; 424 | buildConfigurationList = 232B4C7F1BC29950001F2B7A /* Build configuration list for PBXNativeTarget "KeychainSwift-macOS" */; 425 | buildPhases = ( 426 | 232B4C751BC29950001F2B7A /* Sources */, 427 | 232B4C761BC29950001F2B7A /* Frameworks */, 428 | 232B4C771BC29950001F2B7A /* Headers */, 429 | 232B4C781BC29950001F2B7A /* Resources */, 430 | ); 431 | buildRules = ( 432 | ); 433 | dependencies = ( 434 | ); 435 | name = "KeychainSwift-macOS"; 436 | productName = "KeychainSwift-OSX"; 437 | productReference = 232B4C7A1BC29950001F2B7A /* KeychainSwift.framework */; 438 | productType = "com.apple.product-type.framework"; 439 | }; 440 | 232B4C891BC29984001F2B7A /* KeychainSwift-watchOS */ = { 441 | isa = PBXNativeTarget; 442 | buildConfigurationList = 232B4C8F1BC29984001F2B7A /* Build configuration list for PBXNativeTarget "KeychainSwift-watchOS" */; 443 | buildPhases = ( 444 | 232B4C851BC29984001F2B7A /* Sources */, 445 | 232B4C861BC29984001F2B7A /* Frameworks */, 446 | 232B4C871BC29984001F2B7A /* Headers */, 447 | 232B4C881BC29984001F2B7A /* Resources */, 448 | ); 449 | buildRules = ( 450 | ); 451 | dependencies = ( 452 | ); 453 | name = "KeychainSwift-watchOS"; 454 | productName = "KeychainSwift-watchOS"; 455 | productReference = 232B4C8A1BC29984001F2B7A /* KeychainSwift.framework */; 456 | productType = "com.apple.product-type.framework"; 457 | }; 458 | 23E7855F1BDA410C00B7564A /* KeychainSwift-tvOS */ = { 459 | isa = PBXNativeTarget; 460 | buildConfigurationList = 23E785651BDA410C00B7564A /* Build configuration list for PBXNativeTarget "KeychainSwift-tvOS" */; 461 | buildPhases = ( 462 | 23E7855B1BDA410C00B7564A /* Sources */, 463 | 23E7855C1BDA410C00B7564A /* Frameworks */, 464 | 23E7855D1BDA410C00B7564A /* Headers */, 465 | 23E7855E1BDA410C00B7564A /* Resources */, 466 | ); 467 | buildRules = ( 468 | ); 469 | dependencies = ( 470 | ); 471 | name = "KeychainSwift-tvOS"; 472 | productName = "KeychainSwift-tvOS"; 473 | productReference = 23E785601BDA410C00B7564A /* KeychainSwift.framework */; 474 | productType = "com.apple.product-type.framework"; 475 | }; 476 | 7E3A6B5F1D3F62C2007C5B1F /* macOS Tests */ = { 477 | isa = PBXNativeTarget; 478 | buildConfigurationList = 7E3A6B671D3F62C2007C5B1F /* Build configuration list for PBXNativeTarget "macOS Tests" */; 479 | buildPhases = ( 480 | 7E3A6B5C1D3F62C2007C5B1F /* Sources */, 481 | 7E3A6B5D1D3F62C2007C5B1F /* Frameworks */, 482 | 7E3A6B5E1D3F62C2007C5B1F /* Resources */, 483 | ); 484 | buildRules = ( 485 | ); 486 | dependencies = ( 487 | BF4250112343FE8400F7B44C /* PBXTargetDependency */, 488 | ); 489 | name = "macOS Tests"; 490 | productName = "macOS Tests"; 491 | productReference = 7E3A6B601D3F62C2007C5B1F /* macOS Tests.xctest */; 492 | productType = "com.apple.product-type.bundle.ui-testing"; 493 | }; 494 | 7E3A6B7A1D3F6779007C5B1F /* macOS Demo */ = { 495 | isa = PBXNativeTarget; 496 | buildConfigurationList = 7E3A6B891D3F6779007C5B1F /* Build configuration list for PBXNativeTarget "macOS Demo" */; 497 | buildPhases = ( 498 | 7E3A6B771D3F6779007C5B1F /* Sources */, 499 | 7E3A6B781D3F6779007C5B1F /* Frameworks */, 500 | 7E3A6B791D3F6779007C5B1F /* Resources */, 501 | 7E3A6B8E1D3F67B1007C5B1F /* Embed Frameworks */, 502 | ); 503 | buildRules = ( 504 | ); 505 | dependencies = ( 506 | 7E3A6B8D1D3F67B1007C5B1F /* PBXTargetDependency */, 507 | ); 508 | name = "macOS Demo"; 509 | productName = "macOS Demo"; 510 | productReference = 7E3A6B7B1D3F6779007C5B1F /* macOS Demo.app */; 511 | productType = "com.apple.product-type.application"; 512 | }; 513 | 7ED6C96B1B1C118F00FE8090 /* KeychainSwift */ = { 514 | isa = PBXNativeTarget; 515 | buildConfigurationList = 7ED6C9821B1C118F00FE8090 /* Build configuration list for PBXNativeTarget "KeychainSwift" */; 516 | buildPhases = ( 517 | 7ED6C9671B1C118F00FE8090 /* Sources */, 518 | 7ED6C9681B1C118F00FE8090 /* Frameworks */, 519 | 7ED6C9691B1C118F00FE8090 /* Headers */, 520 | 7ED6C96A1B1C118F00FE8090 /* Resources */, 521 | ); 522 | buildRules = ( 523 | ); 524 | dependencies = ( 525 | ); 526 | name = KeychainSwift; 527 | productName = KeychainSwift; 528 | productReference = 7ED6C96C1B1C118F00FE8090 /* KeychainSwift.framework */; 529 | productType = "com.apple.product-type.framework"; 530 | }; 531 | 7ED6C9761B1C118F00FE8090 /* KeychainSwiftTests */ = { 532 | isa = PBXNativeTarget; 533 | buildConfigurationList = 7ED6C9851B1C118F00FE8090 /* Build configuration list for PBXNativeTarget "KeychainSwiftTests" */; 534 | buildPhases = ( 535 | 7ED6C9731B1C118F00FE8090 /* Sources */, 536 | 7ED6C9741B1C118F00FE8090 /* Frameworks */, 537 | 7ED6C9751B1C118F00FE8090 /* Resources */, 538 | ); 539 | buildRules = ( 540 | ); 541 | dependencies = ( 542 | 7ED6C97A1B1C118F00FE8090 /* PBXTargetDependency */, 543 | 7EC243571E3301D600C771BE /* PBXTargetDependency */, 544 | ); 545 | name = KeychainSwiftTests; 546 | productName = KeychainSwiftTests; 547 | productReference = 7ED6C9771B1C118F00FE8090 /* KeychainSwiftTests.xctest */; 548 | productType = "com.apple.product-type.bundle.unit-test"; 549 | }; 550 | 7ED6C99C1B1C133500FE8090 /* Demo */ = { 551 | isa = PBXNativeTarget; 552 | buildConfigurationList = 7ED6C9B91B1C133500FE8090 /* Build configuration list for PBXNativeTarget "Demo" */; 553 | buildPhases = ( 554 | 7ED6C9991B1C133500FE8090 /* Sources */, 555 | 7ED6C99A1B1C133500FE8090 /* Frameworks */, 556 | 7ED6C99B1B1C133500FE8090 /* Resources */, 557 | 7ED6C9C31B1C13AA00FE8090 /* Embed Frameworks */, 558 | ); 559 | buildRules = ( 560 | ); 561 | dependencies = ( 562 | 7ED6C9C21B1C13AA00FE8090 /* PBXTargetDependency */, 563 | ); 564 | name = Demo; 565 | productName = Demo; 566 | productReference = 7ED6C99D1B1C133500FE8090 /* Keychain Swift.app */; 567 | productType = "com.apple.product-type.application"; 568 | }; 569 | /* End PBXNativeTarget section */ 570 | 571 | /* Begin PBXProject section */ 572 | 7ED6C9631B1C118F00FE8090 /* Project object */ = { 573 | isa = PBXProject; 574 | attributes = { 575 | LastSwiftUpdateCheck = 0800; 576 | LastUpgradeCheck = 1320; 577 | ORGANIZATIONNAME = "Evgenii Neumerzhitckii"; 578 | TargetAttributes = { 579 | 232B4C791BC29950001F2B7A = { 580 | CreatedOnToolsVersion = 7.0.1; 581 | DevelopmentTeam = BS677JRE62; 582 | DevelopmentTeamName = "Evgenii Neumerzhitckii"; 583 | LastSwiftMigration = 0900; 584 | }; 585 | 232B4C891BC29984001F2B7A = { 586 | CreatedOnToolsVersion = 7.0.1; 587 | LastSwiftMigration = 0900; 588 | }; 589 | 23E7855F1BDA410C00B7564A = { 590 | CreatedOnToolsVersion = 7.1; 591 | LastSwiftMigration = 0900; 592 | }; 593 | 7E3A6B5F1D3F62C2007C5B1F = { 594 | CreatedOnToolsVersion = 8.0; 595 | DevelopmentTeam = BS677JRE62; 596 | DevelopmentTeamName = "Evgenii Neumerzhitckii"; 597 | LastSwiftMigration = 0900; 598 | ProvisioningStyle = Automatic; 599 | }; 600 | 7E3A6B7A1D3F6779007C5B1F = { 601 | CreatedOnToolsVersion = 8.0; 602 | DevelopmentTeam = BS677JRE62; 603 | DevelopmentTeamName = "Evgenii Neumerzhitckii"; 604 | LastSwiftMigration = 0900; 605 | ProvisioningStyle = Automatic; 606 | }; 607 | 7ED51A751B7734AF005B39C2 = { 608 | CreatedOnToolsVersion = 7.0; 609 | }; 610 | 7ED6C96B1B1C118F00FE8090 = { 611 | CreatedOnToolsVersion = 6.4; 612 | LastSwiftMigration = 1020; 613 | }; 614 | 7ED6C9761B1C118F00FE8090 = { 615 | CreatedOnToolsVersion = 6.4; 616 | LastSwiftMigration = 1020; 617 | ProvisioningStyle = Manual; 618 | TestTargetID = 7ED6C99C1B1C133500FE8090; 619 | }; 620 | 7ED6C99C1B1C133500FE8090 = { 621 | CreatedOnToolsVersion = 6.4; 622 | LastSwiftMigration = 1020; 623 | }; 624 | }; 625 | }; 626 | buildConfigurationList = 7ED6C9661B1C118F00FE8090 /* Build configuration list for PBXProject "KeychainSwift" */; 627 | compatibilityVersion = "Xcode 3.2"; 628 | developmentRegion = en; 629 | hasScannedForEncodings = 0; 630 | knownRegions = ( 631 | en, 632 | Base, 633 | ); 634 | mainGroup = 7ED6C9621B1C118F00FE8090; 635 | productRefGroup = 7ED6C96D1B1C118F00FE8090 /* Products */; 636 | projectDirPath = ""; 637 | projectRoot = ""; 638 | targets = ( 639 | 7ED6C96B1B1C118F00FE8090 /* KeychainSwift */, 640 | 232B4C791BC29950001F2B7A /* KeychainSwift-macOS */, 641 | 232B4C891BC29984001F2B7A /* KeychainSwift-watchOS */, 642 | 23E7855F1BDA410C00B7564A /* KeychainSwift-tvOS */, 643 | 7ED6C9761B1C118F00FE8090 /* KeychainSwiftTests */, 644 | 7ED6C99C1B1C133500FE8090 /* Demo */, 645 | 7ED51A751B7734AF005B39C2 /* ConcatenateSwiftFiles */, 646 | 7E3A6B5F1D3F62C2007C5B1F /* macOS Tests */, 647 | 7E3A6B7A1D3F6779007C5B1F /* macOS Demo */, 648 | ); 649 | }; 650 | /* End PBXProject section */ 651 | 652 | /* Begin PBXResourcesBuildPhase section */ 653 | 232B4C781BC29950001F2B7A /* Resources */ = { 654 | isa = PBXResourcesBuildPhase; 655 | buildActionMask = 2147483647; 656 | files = ( 657 | ); 658 | runOnlyForDeploymentPostprocessing = 0; 659 | }; 660 | 232B4C881BC29984001F2B7A /* Resources */ = { 661 | isa = PBXResourcesBuildPhase; 662 | buildActionMask = 2147483647; 663 | files = ( 664 | ); 665 | runOnlyForDeploymentPostprocessing = 0; 666 | }; 667 | 23E7855E1BDA410C00B7564A /* Resources */ = { 668 | isa = PBXResourcesBuildPhase; 669 | buildActionMask = 2147483647; 670 | files = ( 671 | ); 672 | runOnlyForDeploymentPostprocessing = 0; 673 | }; 674 | 7E3A6B5E1D3F62C2007C5B1F /* Resources */ = { 675 | isa = PBXResourcesBuildPhase; 676 | buildActionMask = 2147483647; 677 | files = ( 678 | ); 679 | runOnlyForDeploymentPostprocessing = 0; 680 | }; 681 | 7E3A6B791D3F6779007C5B1F /* Resources */ = { 682 | isa = PBXResourcesBuildPhase; 683 | buildActionMask = 2147483647; 684 | files = ( 685 | 7E3A6B821D3F6779007C5B1F /* Assets.xcassets in Resources */, 686 | 7E3A6B851D3F6779007C5B1F /* Main.storyboard in Resources */, 687 | ); 688 | runOnlyForDeploymentPostprocessing = 0; 689 | }; 690 | 7ED6C96A1B1C118F00FE8090 /* Resources */ = { 691 | isa = PBXResourcesBuildPhase; 692 | buildActionMask = 2147483647; 693 | files = ( 694 | ); 695 | runOnlyForDeploymentPostprocessing = 0; 696 | }; 697 | 7ED6C9751B1C118F00FE8090 /* Resources */ = { 698 | isa = PBXResourcesBuildPhase; 699 | buildActionMask = 2147483647; 700 | files = ( 701 | ); 702 | runOnlyForDeploymentPostprocessing = 0; 703 | }; 704 | 7ED6C99B1B1C133500FE8090 /* Resources */ = { 705 | isa = PBXResourcesBuildPhase; 706 | buildActionMask = 2147483647; 707 | files = ( 708 | 7ED6C9A71B1C133500FE8090 /* Main.storyboard in Resources */, 709 | 7ED6C9AC1B1C133500FE8090 /* LaunchScreen.xib in Resources */, 710 | 7ED6C9A91B1C133500FE8090 /* Images.xcassets in Resources */, 711 | ); 712 | runOnlyForDeploymentPostprocessing = 0; 713 | }; 714 | /* End PBXResourcesBuildPhase section */ 715 | 716 | /* Begin PBXSourcesBuildPhase section */ 717 | 232B4C751BC29950001F2B7A /* Sources */ = { 718 | isa = PBXSourcesBuildPhase; 719 | buildActionMask = 2147483647; 720 | files = ( 721 | 232B4C821BC2995D001F2B7A /* KeychainSwift.swift in Sources */, 722 | 232B4C831BC2995D001F2B7A /* KeychainSwiftAccessOptions.swift in Sources */, 723 | 232B4C841BC2995D001F2B7A /* TegKeychainConstants.swift in Sources */, 724 | ); 725 | runOnlyForDeploymentPostprocessing = 0; 726 | }; 727 | 232B4C851BC29984001F2B7A /* Sources */ = { 728 | isa = PBXSourcesBuildPhase; 729 | buildActionMask = 2147483647; 730 | files = ( 731 | 232B4C921BC29991001F2B7A /* KeychainSwift.swift in Sources */, 732 | 232B4C931BC29991001F2B7A /* KeychainSwiftAccessOptions.swift in Sources */, 733 | 232B4C941BC29991001F2B7A /* TegKeychainConstants.swift in Sources */, 734 | ); 735 | runOnlyForDeploymentPostprocessing = 0; 736 | }; 737 | 23E7855B1BDA410C00B7564A /* Sources */ = { 738 | isa = PBXSourcesBuildPhase; 739 | buildActionMask = 2147483647; 740 | files = ( 741 | 23E785681BDA415000B7564A /* KeychainSwift.swift in Sources */, 742 | 23E785691BDA415000B7564A /* KeychainSwiftAccessOptions.swift in Sources */, 743 | 23E7856A1BDA415000B7564A /* TegKeychainConstants.swift in Sources */, 744 | ); 745 | runOnlyForDeploymentPostprocessing = 0; 746 | }; 747 | 7E3A6B5C1D3F62C2007C5B1F /* Sources */ = { 748 | isa = PBXSourcesBuildPhase; 749 | buildActionMask = 2147483647; 750 | files = ( 751 | 508566AA1FA34EB1004208ED /* SynchronizableTests.swift in Sources */, 752 | F271A1AF23BE082500FCC3B9 /* AllKeysTests.swift in Sources */, 753 | 508566A41FA34EB1004208ED /* KeychainSwiftTests.swift in Sources */, 754 | 508566A21FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift in Sources */, 755 | 508566A81FA34EB1004208ED /* macOS_Tests.swift in Sources */, 756 | 5085669C1FA34EB1004208ED /* AccessGroupTests.swift in Sources */, 757 | ); 758 | runOnlyForDeploymentPostprocessing = 0; 759 | }; 760 | 7E3A6B771D3F6779007C5B1F /* Sources */ = { 761 | isa = PBXSourcesBuildPhase; 762 | buildActionMask = 2147483647; 763 | files = ( 764 | 7E3A6B801D3F6779007C5B1F /* ViewController.swift in Sources */, 765 | 7E3A6B7E1D3F6779007C5B1F /* AppDelegate.swift in Sources */, 766 | ); 767 | runOnlyForDeploymentPostprocessing = 0; 768 | }; 769 | 7ED6C9671B1C118F00FE8090 /* Sources */ = { 770 | isa = PBXSourcesBuildPhase; 771 | buildActionMask = 2147483647; 772 | files = ( 773 | 7ED6C9911B1C128100FE8090 /* KeychainSwiftAccessOptions.swift in Sources */, 774 | 7ED6C9901B1C128100FE8090 /* KeychainSwift.swift in Sources */, 775 | 7ED6C9921B1C128100FE8090 /* TegKeychainConstants.swift in Sources */, 776 | ); 777 | runOnlyForDeploymentPostprocessing = 0; 778 | }; 779 | 7ED6C9731B1C118F00FE8090 /* Sources */ = { 780 | isa = PBXSourcesBuildPhase; 781 | buildActionMask = 2147483647; 782 | files = ( 783 | 508566A11FA34EB1004208ED /* KeychainSwiftPrefixedTests.swift in Sources */, 784 | 508566A71FA34EB1004208ED /* macOS_Tests.swift in Sources */, 785 | 7ED6C9971B1C12B300FE8090 /* KeychainSwiftAccessOptions.swift in Sources */, 786 | C7E1DE4C1E4B7C9F003818F6 /* ConcurrencyTests.swift in Sources */, 787 | 7ED6C9961B1C12B100FE8090 /* KeychainSwift.swift in Sources */, 788 | 508566A91FA34EB1004208ED /* SynchronizableTests.swift in Sources */, 789 | 5085669B1FA34EB1004208ED /* AccessGroupTests.swift in Sources */, 790 | 7ED6C9981B1C12B500FE8090 /* TegKeychainConstants.swift in Sources */, 791 | 5085669D1FA34EB1004208ED /* ClearTests.swift in Sources */, 792 | F271A1AE23BE071800FCC3B9 /* AllKeysTests.swift in Sources */, 793 | 508566A31FA34EB1004208ED /* KeychainSwiftTests.swift in Sources */, 794 | ); 795 | runOnlyForDeploymentPostprocessing = 0; 796 | }; 797 | 7ED6C9991B1C133500FE8090 /* Sources */ = { 798 | isa = PBXSourcesBuildPhase; 799 | buildActionMask = 2147483647; 800 | files = ( 801 | 7ED6C9A41B1C133500FE8090 /* ViewController.swift in Sources */, 802 | 7EE5B9A11E32F6D400AA56FF /* KeychainSwiftCBridge.swift in Sources */, 803 | 7ED6C9A21B1C133500FE8090 /* AppDelegate.swift in Sources */, 804 | ); 805 | runOnlyForDeploymentPostprocessing = 0; 806 | }; 807 | /* End PBXSourcesBuildPhase section */ 808 | 809 | /* Begin PBXTargetDependency section */ 810 | 7E3A6B8D1D3F67B1007C5B1F /* PBXTargetDependency */ = { 811 | isa = PBXTargetDependency; 812 | target = 232B4C791BC29950001F2B7A /* KeychainSwift-macOS */; 813 | targetProxy = 7E3A6B8C1D3F67B1007C5B1F /* PBXContainerItemProxy */; 814 | }; 815 | 7EC243571E3301D600C771BE /* PBXTargetDependency */ = { 816 | isa = PBXTargetDependency; 817 | target = 7ED6C99C1B1C133500FE8090 /* Demo */; 818 | targetProxy = 7EC243561E3301D600C771BE /* PBXContainerItemProxy */; 819 | }; 820 | 7ED6C97A1B1C118F00FE8090 /* PBXTargetDependency */ = { 821 | isa = PBXTargetDependency; 822 | target = 7ED6C96B1B1C118F00FE8090 /* KeychainSwift */; 823 | targetProxy = 7ED6C9791B1C118F00FE8090 /* PBXContainerItemProxy */; 824 | }; 825 | 7ED6C9C21B1C13AA00FE8090 /* PBXTargetDependency */ = { 826 | isa = PBXTargetDependency; 827 | target = 7ED6C96B1B1C118F00FE8090 /* KeychainSwift */; 828 | targetProxy = 7ED6C9C11B1C13AA00FE8090 /* PBXContainerItemProxy */; 829 | }; 830 | BF4250112343FE8400F7B44C /* PBXTargetDependency */ = { 831 | isa = PBXTargetDependency; 832 | target = 232B4C791BC29950001F2B7A /* KeychainSwift-macOS */; 833 | targetProxy = BF4250102343FE8400F7B44C /* PBXContainerItemProxy */; 834 | }; 835 | /* End PBXTargetDependency section */ 836 | 837 | /* Begin PBXVariantGroup section */ 838 | 7E3A6B831D3F6779007C5B1F /* Main.storyboard */ = { 839 | isa = PBXVariantGroup; 840 | children = ( 841 | 7E3A6B841D3F6779007C5B1F /* Base */, 842 | ); 843 | name = Main.storyboard; 844 | sourceTree = ""; 845 | }; 846 | 7ED6C9A51B1C133500FE8090 /* Main.storyboard */ = { 847 | isa = PBXVariantGroup; 848 | children = ( 849 | 7ED6C9A61B1C133500FE8090 /* Base */, 850 | ); 851 | name = Main.storyboard; 852 | sourceTree = ""; 853 | }; 854 | 7ED6C9AA1B1C133500FE8090 /* LaunchScreen.xib */ = { 855 | isa = PBXVariantGroup; 856 | children = ( 857 | 7ED6C9AB1B1C133500FE8090 /* Base */, 858 | ); 859 | name = LaunchScreen.xib; 860 | sourceTree = ""; 861 | }; 862 | /* End PBXVariantGroup section */ 863 | 864 | /* Begin XCBuildConfiguration section */ 865 | 232B4C801BC29950001F2B7A /* Debug */ = { 866 | isa = XCBuildConfiguration; 867 | buildSettings = { 868 | APPLICATION_EXTENSION_API_ONLY = YES; 869 | CODE_SIGN_IDENTITY = "-"; 870 | COMBINE_HIDPI_IMAGES = YES; 871 | DEBUG_INFORMATION_FORMAT = dwarf; 872 | DEFINES_MODULE = YES; 873 | DYLIB_COMPATIBILITY_VERSION = 1; 874 | DYLIB_CURRENT_VERSION = 1; 875 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 876 | FRAMEWORK_VERSION = A; 877 | INFOPLIST_FILE = Sources/Info.plist; 878 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 879 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 880 | MACOSX_DEPLOYMENT_TARGET = 10.13; 881 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-macOS"; 882 | PRODUCT_NAME = KeychainSwift; 883 | SDKROOT = macosx; 884 | SKIP_INSTALL = YES; 885 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 886 | SWIFT_VERSION = 5.0; 887 | }; 888 | name = Debug; 889 | }; 890 | 232B4C811BC29950001F2B7A /* Release */ = { 891 | isa = XCBuildConfiguration; 892 | buildSettings = { 893 | APPLICATION_EXTENSION_API_ONLY = YES; 894 | CODE_SIGN_IDENTITY = "-"; 895 | COMBINE_HIDPI_IMAGES = YES; 896 | DEFINES_MODULE = YES; 897 | DYLIB_COMPATIBILITY_VERSION = 1; 898 | DYLIB_CURRENT_VERSION = 1; 899 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 900 | FRAMEWORK_VERSION = A; 901 | INFOPLIST_FILE = Sources/Info.plist; 902 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 903 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 904 | MACOSX_DEPLOYMENT_TARGET = 10.13; 905 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-macOS"; 906 | PRODUCT_NAME = KeychainSwift; 907 | SDKROOT = macosx; 908 | SKIP_INSTALL = YES; 909 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 910 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 911 | SWIFT_VERSION = 5.0; 912 | }; 913 | name = Release; 914 | }; 915 | 232B4C901BC29984001F2B7A /* Debug */ = { 916 | isa = XCBuildConfiguration; 917 | buildSettings = { 918 | APPLICATION_EXTENSION_API_ONLY = YES; 919 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 920 | DEBUG_INFORMATION_FORMAT = dwarf; 921 | DEFINES_MODULE = YES; 922 | DYLIB_COMPATIBILITY_VERSION = 1; 923 | DYLIB_CURRENT_VERSION = 1; 924 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 925 | INFOPLIST_FILE = Sources/Info.plist; 926 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 927 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 928 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-watchOS"; 929 | PRODUCT_NAME = KeychainSwift; 930 | SDKROOT = watchos; 931 | SKIP_INSTALL = YES; 932 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 933 | SWIFT_VERSION = 5.0; 934 | TARGETED_DEVICE_FAMILY = 4; 935 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 936 | }; 937 | name = Debug; 938 | }; 939 | 232B4C911BC29984001F2B7A /* Release */ = { 940 | isa = XCBuildConfiguration; 941 | buildSettings = { 942 | APPLICATION_EXTENSION_API_ONLY = YES; 943 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 944 | DEFINES_MODULE = YES; 945 | DYLIB_COMPATIBILITY_VERSION = 1; 946 | DYLIB_CURRENT_VERSION = 1; 947 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 948 | INFOPLIST_FILE = Sources/Info.plist; 949 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 950 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 951 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-watchOS"; 952 | PRODUCT_NAME = KeychainSwift; 953 | SDKROOT = watchos; 954 | SKIP_INSTALL = YES; 955 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 956 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 957 | SWIFT_VERSION = 5.0; 958 | TARGETED_DEVICE_FAMILY = 4; 959 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 960 | }; 961 | name = Release; 962 | }; 963 | 23E785661BDA410C00B7564A /* Debug */ = { 964 | isa = XCBuildConfiguration; 965 | buildSettings = { 966 | APPLICATION_EXTENSION_API_ONLY = YES; 967 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 968 | DEBUG_INFORMATION_FORMAT = dwarf; 969 | DEFINES_MODULE = YES; 970 | DYLIB_COMPATIBILITY_VERSION = 1; 971 | DYLIB_CURRENT_VERSION = 1; 972 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 973 | INFOPLIST_FILE = Sources/Info.plist; 974 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 975 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 976 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-tvOS"; 977 | PRODUCT_NAME = KeychainSwift; 978 | SDKROOT = appletvos; 979 | SKIP_INSTALL = YES; 980 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 981 | SWIFT_VERSION = 5.0; 982 | TARGETED_DEVICE_FAMILY = 3; 983 | TVOS_DEPLOYMENT_TARGET = 12.0; 984 | }; 985 | name = Debug; 986 | }; 987 | 23E785671BDA410C00B7564A /* Release */ = { 988 | isa = XCBuildConfiguration; 989 | buildSettings = { 990 | APPLICATION_EXTENSION_API_ONLY = YES; 991 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 992 | DEFINES_MODULE = YES; 993 | DYLIB_COMPATIBILITY_VERSION = 1; 994 | DYLIB_CURRENT_VERSION = 1; 995 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 996 | INFOPLIST_FILE = Sources/Info.plist; 997 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 998 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 999 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.KeychainSwift-tvOS"; 1000 | PRODUCT_NAME = KeychainSwift; 1001 | SDKROOT = appletvos; 1002 | SKIP_INSTALL = YES; 1003 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1004 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 1005 | SWIFT_VERSION = 5.0; 1006 | TARGETED_DEVICE_FAMILY = 3; 1007 | TVOS_DEPLOYMENT_TARGET = 12.0; 1008 | }; 1009 | name = Release; 1010 | }; 1011 | 7E3A6B651D3F62C2007C5B1F /* Debug */ = { 1012 | isa = XCBuildConfiguration; 1013 | buildSettings = { 1014 | CLANG_ANALYZER_NONNULL = YES; 1015 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1016 | CODE_SIGN_IDENTITY = "-"; 1017 | COMBINE_HIDPI_IMAGES = YES; 1018 | DEBUG_INFORMATION_FORMAT = dwarf; 1019 | INFOPLIST_FILE = "Tests/KeychainSwiftTests/macOS Tests/Info.plist"; 1020 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1021 | MACOSX_DEPLOYMENT_TARGET = 10.13; 1022 | PRODUCT_BUNDLE_IDENTIFIER = "com.evgenii.macOS-Tests"; 1023 | PRODUCT_NAME = "$(TARGET_NAME)"; 1024 | SDKROOT = macosx; 1025 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1026 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 1027 | SWIFT_VERSION = 5.0; 1028 | }; 1029 | name = Debug; 1030 | }; 1031 | 7E3A6B661D3F62C2007C5B1F /* Release */ = { 1032 | isa = XCBuildConfiguration; 1033 | buildSettings = { 1034 | CLANG_ANALYZER_NONNULL = YES; 1035 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1036 | CODE_SIGN_IDENTITY = "-"; 1037 | COMBINE_HIDPI_IMAGES = YES; 1038 | INFOPLIST_FILE = "Tests/KeychainSwiftTests/macOS Tests/Info.plist"; 1039 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1040 | MACOSX_DEPLOYMENT_TARGET = 10.13; 1041 | PRODUCT_BUNDLE_IDENTIFIER = "com.evgenii.macOS-Tests"; 1042 | PRODUCT_NAME = "$(TARGET_NAME)"; 1043 | SDKROOT = macosx; 1044 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1045 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 1046 | SWIFT_VERSION = 5.0; 1047 | }; 1048 | name = Release; 1049 | }; 1050 | 7E3A6B871D3F6779007C5B1F /* Debug */ = { 1051 | isa = XCBuildConfiguration; 1052 | buildSettings = { 1053 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1054 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1055 | CLANG_ANALYZER_NONNULL = YES; 1056 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1057 | CODE_SIGN_IDENTITY = "-"; 1058 | COMBINE_HIDPI_IMAGES = YES; 1059 | DEBUG_INFORMATION_FORMAT = dwarf; 1060 | INFOPLIST_FILE = "macOS Demo/Info.plist"; 1061 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 1062 | MACOSX_DEPLOYMENT_TARGET = 10.13; 1063 | PRODUCT_BUNDLE_IDENTIFIER = "com.evgenii.macOS-Demo"; 1064 | PRODUCT_NAME = "$(TARGET_NAME)"; 1065 | SDKROOT = macosx; 1066 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1067 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 1068 | SWIFT_VERSION = 5.0; 1069 | }; 1070 | name = Debug; 1071 | }; 1072 | 7E3A6B881D3F6779007C5B1F /* Release */ = { 1073 | isa = XCBuildConfiguration; 1074 | buildSettings = { 1075 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1076 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1077 | CLANG_ANALYZER_NONNULL = YES; 1078 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1079 | CODE_SIGN_IDENTITY = "-"; 1080 | COMBINE_HIDPI_IMAGES = YES; 1081 | INFOPLIST_FILE = "macOS Demo/Info.plist"; 1082 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 1083 | MACOSX_DEPLOYMENT_TARGET = 10.13; 1084 | PRODUCT_BUNDLE_IDENTIFIER = "com.evgenii.macOS-Demo"; 1085 | PRODUCT_NAME = "$(TARGET_NAME)"; 1086 | SDKROOT = macosx; 1087 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1088 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 1089 | SWIFT_VERSION = 5.0; 1090 | }; 1091 | name = Release; 1092 | }; 1093 | 7ED51A761B7734AF005B39C2 /* Debug */ = { 1094 | isa = XCBuildConfiguration; 1095 | buildSettings = { 1096 | DEBUGGING_SYMBOLS = YES; 1097 | DEBUG_INFORMATION_FORMAT = dwarf; 1098 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 1099 | GCC_OPTIMIZATION_LEVEL = 0; 1100 | OTHER_CFLAGS = ""; 1101 | OTHER_LDFLAGS = ""; 1102 | PRODUCT_NAME = "$(TARGET_NAME)"; 1103 | }; 1104 | name = Debug; 1105 | }; 1106 | 7ED51A771B7734AF005B39C2 /* Release */ = { 1107 | isa = XCBuildConfiguration; 1108 | buildSettings = { 1109 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1110 | OTHER_CFLAGS = ""; 1111 | OTHER_LDFLAGS = ""; 1112 | PRODUCT_NAME = "$(TARGET_NAME)"; 1113 | }; 1114 | name = Release; 1115 | }; 1116 | 7ED6C9801B1C118F00FE8090 /* Debug */ = { 1117 | isa = XCBuildConfiguration; 1118 | buildSettings = { 1119 | ALWAYS_SEARCH_USER_PATHS = NO; 1120 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 1121 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1122 | CLANG_CXX_LIBRARY = "libc++"; 1123 | CLANG_ENABLE_MODULES = YES; 1124 | CLANG_ENABLE_OBJC_ARC = YES; 1125 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1126 | CLANG_WARN_BOOL_CONVERSION = YES; 1127 | CLANG_WARN_COMMA = YES; 1128 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1129 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 1130 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1131 | CLANG_WARN_EMPTY_BODY = YES; 1132 | CLANG_WARN_ENUM_CONVERSION = YES; 1133 | CLANG_WARN_INFINITE_RECURSION = YES; 1134 | CLANG_WARN_INT_CONVERSION = YES; 1135 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1136 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 1137 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1138 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1139 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 1140 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1141 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1142 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1143 | CLANG_WARN_UNREACHABLE_CODE = YES; 1144 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1145 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1146 | COPY_PHASE_STRIP = NO; 1147 | CURRENT_PROJECT_VERSION = 1; 1148 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1149 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1150 | ENABLE_TESTABILITY = YES; 1151 | GCC_C_LANGUAGE_STANDARD = gnu99; 1152 | GCC_DYNAMIC_NO_PIC = NO; 1153 | GCC_NO_COMMON_BLOCKS = YES; 1154 | GCC_OPTIMIZATION_LEVEL = 0; 1155 | GCC_PREPROCESSOR_DEFINITIONS = ( 1156 | "DEBUG=1", 1157 | "$(inherited)", 1158 | ); 1159 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 1160 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1161 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1162 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1163 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1164 | GCC_WARN_UNUSED_FUNCTION = YES; 1165 | GCC_WARN_UNUSED_VARIABLE = YES; 1166 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 1167 | MTL_ENABLE_DEBUG_INFO = YES; 1168 | ONLY_ACTIVE_ARCH = YES; 1169 | SDKROOT = iphoneos; 1170 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1171 | SWIFT_VERSION = 5.0; 1172 | TARGETED_DEVICE_FAMILY = "1,2"; 1173 | VERSIONING_SYSTEM = "apple-generic"; 1174 | VERSION_INFO_PREFIX = ""; 1175 | }; 1176 | name = Debug; 1177 | }; 1178 | 7ED6C9811B1C118F00FE8090 /* Release */ = { 1179 | isa = XCBuildConfiguration; 1180 | buildSettings = { 1181 | ALWAYS_SEARCH_USER_PATHS = NO; 1182 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 1183 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1184 | CLANG_CXX_LIBRARY = "libc++"; 1185 | CLANG_ENABLE_MODULES = YES; 1186 | CLANG_ENABLE_OBJC_ARC = YES; 1187 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1188 | CLANG_WARN_BOOL_CONVERSION = YES; 1189 | CLANG_WARN_COMMA = YES; 1190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1191 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 1192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1193 | CLANG_WARN_EMPTY_BODY = YES; 1194 | CLANG_WARN_ENUM_CONVERSION = YES; 1195 | CLANG_WARN_INFINITE_RECURSION = YES; 1196 | CLANG_WARN_INT_CONVERSION = YES; 1197 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1198 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 1199 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1201 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 1202 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1203 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1205 | CLANG_WARN_UNREACHABLE_CODE = YES; 1206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1207 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1208 | COPY_PHASE_STRIP = NO; 1209 | CURRENT_PROJECT_VERSION = 1; 1210 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1211 | ENABLE_NS_ASSERTIONS = NO; 1212 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1213 | GCC_C_LANGUAGE_STANDARD = gnu99; 1214 | GCC_NO_COMMON_BLOCKS = YES; 1215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1217 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1219 | GCC_WARN_UNUSED_FUNCTION = YES; 1220 | GCC_WARN_UNUSED_VARIABLE = YES; 1221 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 1222 | MTL_ENABLE_DEBUG_INFO = NO; 1223 | SDKROOT = iphoneos; 1224 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1225 | SWIFT_VERSION = 5.0; 1226 | TARGETED_DEVICE_FAMILY = "1,2"; 1227 | VALIDATE_PRODUCT = YES; 1228 | VERSIONING_SYSTEM = "apple-generic"; 1229 | VERSION_INFO_PREFIX = ""; 1230 | }; 1231 | name = Release; 1232 | }; 1233 | 7ED6C9831B1C118F00FE8090 /* Debug */ = { 1234 | isa = XCBuildConfiguration; 1235 | buildSettings = { 1236 | APPLICATION_EXTENSION_API_ONLY = YES; 1237 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 1238 | DEFINES_MODULE = YES; 1239 | DYLIB_COMPATIBILITY_VERSION = 1; 1240 | DYLIB_CURRENT_VERSION = 1; 1241 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1242 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 1243 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1244 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1245 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.$(PRODUCT_NAME:rfc1034identifier)"; 1246 | PRODUCT_NAME = "$(TARGET_NAME)"; 1247 | SKIP_INSTALL = YES; 1248 | SWIFT_VERSION = 5.0; 1249 | }; 1250 | name = Debug; 1251 | }; 1252 | 7ED6C9841B1C118F00FE8090 /* Release */ = { 1253 | isa = XCBuildConfiguration; 1254 | buildSettings = { 1255 | APPLICATION_EXTENSION_API_ONLY = YES; 1256 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 1257 | DEFINES_MODULE = YES; 1258 | DYLIB_COMPATIBILITY_VERSION = 1; 1259 | DYLIB_CURRENT_VERSION = 1; 1260 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1261 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 1262 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1263 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1264 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.$(PRODUCT_NAME:rfc1034identifier)"; 1265 | PRODUCT_NAME = "$(TARGET_NAME)"; 1266 | SKIP_INSTALL = YES; 1267 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1268 | SWIFT_VERSION = 5.0; 1269 | }; 1270 | name = Release; 1271 | }; 1272 | 7ED6C9861B1C118F00FE8090 /* Debug */ = { 1273 | isa = XCBuildConfiguration; 1274 | buildSettings = { 1275 | CLANG_ENABLE_MODULES = YES; 1276 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 1277 | GCC_PREPROCESSOR_DEFINITIONS = ( 1278 | "DEBUG=1", 1279 | "$(inherited)", 1280 | ); 1281 | INFOPLIST_FILE = Tests/KeychainSwiftTests/Info.plist; 1282 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1283 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.$(PRODUCT_NAME:rfc1034identifier)"; 1284 | PRODUCT_NAME = "$(TARGET_NAME)"; 1285 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/KeychainSwiftTests/KeychainSwiftTests-Bridging-Header.h"; 1286 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1287 | SWIFT_VERSION = 5.0; 1288 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Keychain Swift.app/Keychain Swift"; 1289 | }; 1290 | name = Debug; 1291 | }; 1292 | 7ED6C9871B1C118F00FE8090 /* Release */ = { 1293 | isa = XCBuildConfiguration; 1294 | buildSettings = { 1295 | CLANG_ENABLE_MODULES = YES; 1296 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 1297 | INFOPLIST_FILE = Tests/KeychainSwiftTests/Info.plist; 1298 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1299 | PRODUCT_BUNDLE_IDENTIFIER = "com.marketplacer.$(PRODUCT_NAME:rfc1034identifier)"; 1300 | PRODUCT_NAME = "$(TARGET_NAME)"; 1301 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/KeychainSwiftTests/KeychainSwiftTests-Bridging-Header.h"; 1302 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1303 | SWIFT_VERSION = 5.0; 1304 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Keychain Swift.app/Keychain Swift"; 1305 | }; 1306 | name = Release; 1307 | }; 1308 | 7ED6C9BA1B1C133500FE8090 /* Debug */ = { 1309 | isa = XCBuildConfiguration; 1310 | buildSettings = { 1311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1312 | GCC_PREPROCESSOR_DEFINITIONS = ( 1313 | "DEBUG=1", 1314 | "$(inherited)", 1315 | ); 1316 | INFOPLIST_FILE = Demo/Info.plist; 1317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 1318 | PRODUCT_BUNDLE_IDENTIFIER = com.marketplacer.KeychainSwiftDemo; 1319 | PRODUCT_NAME = "Keychain Swift"; 1320 | SWIFT_VERSION = 5.0; 1321 | }; 1322 | name = Debug; 1323 | }; 1324 | 7ED6C9BB1B1C133500FE8090 /* Release */ = { 1325 | isa = XCBuildConfiguration; 1326 | buildSettings = { 1327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1328 | INFOPLIST_FILE = Demo/Info.plist; 1329 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 1330 | PRODUCT_BUNDLE_IDENTIFIER = com.marketplacer.KeychainSwiftDemo; 1331 | PRODUCT_NAME = "Keychain Swift"; 1332 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1333 | SWIFT_VERSION = 5.0; 1334 | }; 1335 | name = Release; 1336 | }; 1337 | /* End XCBuildConfiguration section */ 1338 | 1339 | /* Begin XCConfigurationList section */ 1340 | 232B4C7F1BC29950001F2B7A /* Build configuration list for PBXNativeTarget "KeychainSwift-macOS" */ = { 1341 | isa = XCConfigurationList; 1342 | buildConfigurations = ( 1343 | 232B4C801BC29950001F2B7A /* Debug */, 1344 | 232B4C811BC29950001F2B7A /* Release */, 1345 | ); 1346 | defaultConfigurationIsVisible = 0; 1347 | defaultConfigurationName = Release; 1348 | }; 1349 | 232B4C8F1BC29984001F2B7A /* Build configuration list for PBXNativeTarget "KeychainSwift-watchOS" */ = { 1350 | isa = XCConfigurationList; 1351 | buildConfigurations = ( 1352 | 232B4C901BC29984001F2B7A /* Debug */, 1353 | 232B4C911BC29984001F2B7A /* Release */, 1354 | ); 1355 | defaultConfigurationIsVisible = 0; 1356 | defaultConfigurationName = Release; 1357 | }; 1358 | 23E785651BDA410C00B7564A /* Build configuration list for PBXNativeTarget "KeychainSwift-tvOS" */ = { 1359 | isa = XCConfigurationList; 1360 | buildConfigurations = ( 1361 | 23E785661BDA410C00B7564A /* Debug */, 1362 | 23E785671BDA410C00B7564A /* Release */, 1363 | ); 1364 | defaultConfigurationIsVisible = 0; 1365 | defaultConfigurationName = Release; 1366 | }; 1367 | 7E3A6B671D3F62C2007C5B1F /* Build configuration list for PBXNativeTarget "macOS Tests" */ = { 1368 | isa = XCConfigurationList; 1369 | buildConfigurations = ( 1370 | 7E3A6B651D3F62C2007C5B1F /* Debug */, 1371 | 7E3A6B661D3F62C2007C5B1F /* Release */, 1372 | ); 1373 | defaultConfigurationIsVisible = 0; 1374 | defaultConfigurationName = Release; 1375 | }; 1376 | 7E3A6B891D3F6779007C5B1F /* Build configuration list for PBXNativeTarget "macOS Demo" */ = { 1377 | isa = XCConfigurationList; 1378 | buildConfigurations = ( 1379 | 7E3A6B871D3F6779007C5B1F /* Debug */, 1380 | 7E3A6B881D3F6779007C5B1F /* Release */, 1381 | ); 1382 | defaultConfigurationIsVisible = 0; 1383 | defaultConfigurationName = Release; 1384 | }; 1385 | 7ED51A781B7734AF005B39C2 /* Build configuration list for PBXLegacyTarget "ConcatenateSwiftFiles" */ = { 1386 | isa = XCConfigurationList; 1387 | buildConfigurations = ( 1388 | 7ED51A761B7734AF005B39C2 /* Debug */, 1389 | 7ED51A771B7734AF005B39C2 /* Release */, 1390 | ); 1391 | defaultConfigurationIsVisible = 0; 1392 | defaultConfigurationName = Release; 1393 | }; 1394 | 7ED6C9661B1C118F00FE8090 /* Build configuration list for PBXProject "KeychainSwift" */ = { 1395 | isa = XCConfigurationList; 1396 | buildConfigurations = ( 1397 | 7ED6C9801B1C118F00FE8090 /* Debug */, 1398 | 7ED6C9811B1C118F00FE8090 /* Release */, 1399 | ); 1400 | defaultConfigurationIsVisible = 0; 1401 | defaultConfigurationName = Release; 1402 | }; 1403 | 7ED6C9821B1C118F00FE8090 /* Build configuration list for PBXNativeTarget "KeychainSwift" */ = { 1404 | isa = XCConfigurationList; 1405 | buildConfigurations = ( 1406 | 7ED6C9831B1C118F00FE8090 /* Debug */, 1407 | 7ED6C9841B1C118F00FE8090 /* Release */, 1408 | ); 1409 | defaultConfigurationIsVisible = 0; 1410 | defaultConfigurationName = Release; 1411 | }; 1412 | 7ED6C9851B1C118F00FE8090 /* Build configuration list for PBXNativeTarget "KeychainSwiftTests" */ = { 1413 | isa = XCConfigurationList; 1414 | buildConfigurations = ( 1415 | 7ED6C9861B1C118F00FE8090 /* Debug */, 1416 | 7ED6C9871B1C118F00FE8090 /* Release */, 1417 | ); 1418 | defaultConfigurationIsVisible = 0; 1419 | defaultConfigurationName = Release; 1420 | }; 1421 | 7ED6C9B91B1C133500FE8090 /* Build configuration list for PBXNativeTarget "Demo" */ = { 1422 | isa = XCConfigurationList; 1423 | buildConfigurations = ( 1424 | 7ED6C9BA1B1C133500FE8090 /* Debug */, 1425 | 7ED6C9BB1B1C133500FE8090 /* Release */, 1426 | ); 1427 | defaultConfigurationIsVisible = 0; 1428 | defaultConfigurationName = Release; 1429 | }; 1430 | /* End XCConfigurationList section */ 1431 | }; 1432 | rootObject = 7ED6C9631B1C118F00FE8090 /* Project object */; 1433 | } 1434 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/ConcatenateSwiftFiles.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 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/KeychainSwift-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/KeychainSwift-tvOS.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 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/KeychainSwift-watchOS.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 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/KeychainSwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/macOS Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /KeychainSwift.xcodeproj/xcshareddata/xcschemes/macOS Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 - 2024 Evgenii Neumerzhitckii 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "KeychainSwift", 6 | products: [ 7 | .library(name: "KeychainSwift", targets: ["KeychainSwift"]), 8 | ], 9 | dependencies: [ 10 | ], 11 | targets: [ 12 | .target( 13 | name: "KeychainSwift", 14 | dependencies: [], 15 | path: "Sources", 16 | exclude: ["Info.plist"] 17 | ), 18 | .testTarget( 19 | name: "KeychainSwiftTests", 20 | dependencies: ["KeychainSwift"], 21 | exclude: ["ClearTests.swift"] 22 | ) 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Helper functions for storing text in Keychain for iOS, macOS, tvOS and WatchOS 2 | 3 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![CocoaPods Version](https://img.shields.io/cocoapods/v/KeychainSwift.svg?style=flat)](http://cocoadocs.org/docsets/KeychainSwift) 5 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 6 | [![License](https://img.shields.io/cocoapods/l/KeychainSwift.svg?style=flat)](http://cocoadocs.org/docsets/KeychainSwift) 7 | [![Platform](https://img.shields.io/cocoapods/p/KeychainSwift.svg?style=flat)](http://cocoadocs.org/docsets/KeychainSwift) 8 | 9 | This is a collection of helper functions for saving text and data in the Keychain. 10 | As you probably noticed Apple's keychain API is a bit verbose. This library was designed to provide shorter syntax for accomplishing a simple task: reading/writing text values for specified keys: 11 | 12 | ```Swift 13 | let keychain = KeychainSwift() 14 | keychain.set("hello world", forKey: "my key") 15 | keychain.get("my key") 16 | ``` 17 | 18 | The Keychain library includes the following features: 19 | 20 | * Get, set and delete string, boolean and Data Keychain items 21 | * Specify item access security level 22 | * Synchronize items through iCloud 23 | * Share Keychain items with other apps 24 | 25 | ## What's Keychain? 26 | 27 | Keychain is a secure storage. You can store all kind of sensitive data in it: user passwords, credit card numbers, secret tokens etc. Once stored in Keychain this information is only available to your app, other apps can't see it. Besides that, operating system makes sure this information is kept and processed securely. For example, text stored in Keychain can not be extracted from iPhone backup or from its file system. Apple recommends storing only small amount of data in the Keychain. If you need to secure something big you can encrypt it manually, save to a file and store the key in the Keychain. 28 | 29 | 30 | ## Setup 31 | 32 | There are four ways you can add KeychainSwift to your project. 33 | 34 | #### Add source (iOS 7+) 35 | 36 | Simply add [KeychainSwiftDistrib.swift](https://github.com/evgenyneu/keychain-swift/blob/master/Distrib/KeychainSwiftDistrib.swift) file into your Xcode project. 37 | 38 | #### Setup with Carthage (iOS 8+) 39 | 40 | Alternatively, add `github "evgenyneu/keychain-swift" ~> 24.0` to your Cartfile and run `carthage update`. 41 | 42 | #### Setup with CocoaPods (iOS 8+) 43 | 44 | If you are using CocoaPods add this text to your Podfile and run `pod install`. 45 | 46 | ``` 47 | use_frameworks! 48 | target 'Your target name' 49 | pod 'KeychainSwift', '~> 24.0' 50 | ``` 51 | 52 | 53 | #### Setup with Swift Package Manager (in project) 54 | 55 | * In Xcode select *File > Add Packages*. 56 | * Enter this project's URL: https://github.com/evgenyneu/keychain-swift.git 57 | 58 | #### Setup with Swift Package Manager (in Swift Package) 59 | 60 | If you're using KeychainSwift in a Swift package, make sure to specify a `name`. This is because SPM cannot automatically resolve a name for a package that has a different Target name in its `Package.swift` (namely `KeychainSwift`) that differs from the repo link (`keychain-swift`). 61 | 62 | ``` 63 | .package(name: "KeychainSwift", url: "https://github.com/evgenyneu/keychain-swift.git", from: "24.0.0") 64 | ``` 65 | 66 | ## Legacy Swift versions 67 | 68 | Setup a [previous version](https://github.com/evgenyneu/keychain-swift/wiki/Legacy-Swift-versions) of the library if you use an older version of Swift. 69 | 70 | 71 | ## Usage 72 | 73 | Add `import KeychainSwift` to your source code unless you used the file setup method. 74 | 75 | #### String values 76 | 77 | ```Swift 78 | let keychain = KeychainSwift() 79 | keychain.set("hello world", forKey: "my key") 80 | keychain.get("my key") 81 | ``` 82 | 83 | #### Boolean values 84 | 85 | 86 | ```Swift 87 | let keychain = KeychainSwift() 88 | keychain.set(true, forKey: "my key") 89 | keychain.getBool("my key") 90 | ``` 91 | 92 | #### Data values 93 | 94 | ```Swift 95 | let keychain = KeychainSwift() 96 | keychain.set(dataObject, forKey: "my key") 97 | keychain.getData("my key") 98 | ``` 99 | 100 | #### Removing keys from Keychain 101 | 102 | ```Swift 103 | keychain.delete("my key") // Remove single key 104 | keychain.clear() // Delete everything from app's Keychain. Does not work on macOS. 105 | ``` 106 | 107 | #### Return all keys 108 | 109 | ```Swift 110 | let keychain = KeychainSwift() 111 | keychain.allKeys // Returns the names of all keys 112 | ``` 113 | 114 | ## Advanced options 115 | 116 |

Keychain item access

117 | 118 | Use `withAccess` parameter to specify the security level of the keychain storage. 119 | By default the `.accessibleWhenUnlocked` option is used. It is one of the most restrictive options and provides good data protection. 120 | 121 | ``` 122 | let keychain = KeychainSwift() 123 | keychain.set("Hello world", forKey: "key 1", withAccess: .accessibleWhenUnlocked) 124 | ``` 125 | 126 | You can use `.accessibleAfterFirstUnlock` if you need your app to access the keychain item while in the background. Note that it is less secure than the `.accessibleWhenUnlocked` option. 127 | 128 | See the list of all available [access options](https://github.com/evgenyneu/keychain-swift/blob/master/Sources/KeychainSwiftAccessOptions.swift). 129 | 130 | 131 |

Synchronizing keychain items with other devices

132 | 133 | Set `synchronizable` property to `true` to enable keychain items synchronization across user's multiple devices. The synchronization will work for users who have the "Keychain" enabled in the iCloud settings on their devices. 134 | 135 | Setting `synchronizable` property to `true` will add the item to other devices with the `set` method and obtain synchronizable items with the `get` command. Deleting a synchronizable item will remove it from all devices. 136 | 137 | Note that you do NOT need to enable iCloud or Keychain Sharing capabilities in your app's target for this feature to work. 138 | 139 | 140 | ```Swift 141 | // First device 142 | let keychain = KeychainSwift() 143 | keychain.synchronizable = true 144 | keychain.set("hello world", forKey: "my key") 145 | 146 | // Second device 147 | let keychain = KeychainSwift() 148 | keychain.synchronizable = true 149 | keychain.get("my key") // Returns "hello world" 150 | ``` 151 | 152 | We could not get the Keychain synchronization work on macOS. 153 | 154 | 155 |

Sharing keychain items with other apps

156 | 157 | In order to share keychain items between apps on the same device they need to have common *Keychain Groups* registered in *Capabilities > Keychain Sharing* settings. [This tutorial](http://evgenii.com/blog/sharing-keychain-in-ios/) shows how to set it up. 158 | 159 | Use `accessGroup` property to access shared keychain items. In the following example we specify an access group "CS671JRA62.com.myapp.KeychainGroup" that will be used to set, get and delete an item "my key". 160 | 161 | ```Swift 162 | let keychain = KeychainSwift() 163 | keychain.accessGroup = "CS671JRA62.com.myapp.KeychainGroup" // Use your own access goup 164 | 165 | keychain.set("hello world", forKey: "my key") 166 | keychain.get("my key") 167 | keychain.delete("my key") 168 | keychain.clear() 169 | ``` 170 | 171 | *Note*: there is no way of sharing a keychain item between the watchOS 2.0 and its paired device: https://forums.developer.apple.com/thread/5938 172 | 173 | ### Setting key prefix 174 | 175 | One can pass a `keyPrefix` argument when initializing a `KeychainSwift` object. The string passed in `keyPrefix` argument will be used as a prefix to **all the keys** used in `set`, `get`, `getData` and `delete` methods. Adding a prefix to the keychain keys can be useful in unit tests. This prevents the tests from changing the Keychain keys that are used when the app is launched manually. 176 | 177 | Note that `clear` method still clears everything from the Keychain regardless of the prefix used. 178 | 179 | 180 | ```Swift 181 | let keychain = KeychainSwift(keyPrefix: "myTestKey_") 182 | keychain.set("hello world", forKey: "hello") 183 | // Value will be stored under "myTestKey_hello" key 184 | ``` 185 | 186 | ### Check if operation was successful 187 | 188 | One can verify if `set`, `delete` and `clear` methods finished successfully by checking their return values. Those methods return `true` on success and `false` on error. 189 | 190 | ```Swift 191 | if keychain.set("hello world", forKey: "my key") { 192 | // Keychain item is saved successfully 193 | } else { 194 | // Report error 195 | } 196 | ``` 197 | 198 | To get a specific failure reason use the `lastResultCode` property containing result code for the last operation. See [Keychain Result Codes](https://developer.apple.com/documentation/security/1542001-security_framework_result_codes). 199 | 200 | ```Swift 201 | keychain.set("hello world", forKey: "my key") 202 | if keychain.lastResultCode != noErr { /* Report error */ } 203 | ``` 204 | 205 | ### Returning data as reference 206 | 207 | Use the `asReference: true` parameter to return the data as reference, which is needed for [NEVPNProtocol](https://developer.apple.com/documentation/networkextension/nevpnprotocol). 208 | 209 | ```Swift 210 | let keychain = KeychainSwift() 211 | keychain.set(dataObject, forKey: "my key") 212 | keychain.getData("my key", asReference: true) 213 | ``` 214 | 215 | ## Using KeychainSwift from Objective-C 216 | 217 | [This manual](https://github.com/evgenyneu/keychain-swift/wiki/Using-KeychainSwift-in-Objective-C-project) describes how to use KeychainSwift in Objective-C apps. 218 | 219 | ## ❗️Known critical issue - call to action❗️ 220 | 221 | It [has been reported](https://github.com/evgenyneu/keychain-swift/issues/15) that the library sometimes returns `nil` instead of the stored Keychain value. It may be connected with [the Keychain issue](https://forums.developer.apple.com/thread/4743) reported on Apple developer forums. The issue is random and hard to reproduce. If you experienced this problem feel free to create an issue and share your story, so we can find solutions. 222 | 223 | ## Video tutorial 224 | 225 | Thanks to Alex Nagy from [rebeloper.com](https://rebeloper.com/) for creating this two-part [video tutorial](https://www.youtube.com/watch?v=1R-VIzjD4yo&list=PL_csAAO9PQ8bLfPF7JsnF-t4q63WKZ9O9). 226 | 227 | Keychain Swift video tutorial 228 | 229 | ## Demo app 230 | 231 | Keychain Swift demo app 232 | 233 | ## Alternative solutions 234 | 235 | Here are some other Keychain libraries. 236 | 237 | * [DanielTomlinson/Latch](https://github.com/DanielTomlinson/Latch) 238 | * [jrendel/SwiftKeychainWrapper](https://github.com/jrendel/SwiftKeychainWrapper) 239 | * [kishikawakatsumi/KeychainAccess](https://github.com/kishikawakatsumi/KeychainAccess) 240 | * [matthewpalmer/Locksmith](https://github.com/matthewpalmer/Locksmith) 241 | * [s-aska/KeyClip](https://github.com/s-aska/KeyClip) 242 | * [yankodimitrov/SwiftKeychain](https://github.com/yankodimitrov/SwiftKeychain) 243 | 244 | ## Thanks 👍 245 | 246 | * The code is based on this example: [https://gist.github.com/s-aska/e7ad24175fb7b04f78e7](https://gist.github.com/s-aska/e7ad24175fb7b04f78e7) 247 | * Thanks to [diogoguimaraes](https://github.com/diogoguimaraes) for adding Swift Package Manager setup option. 248 | * Thanks to [glyuck](https://github.com/glyuck) for taming booleans. 249 | * Thanks to [pepibumur](https://github.com/pepibumur) for adding macOS, watchOS and tvOS support. 250 | * Thanks to [ezura](https://github.com/ezura) for iOS 7 support. 251 | * Thanks to [mikaoj](https://github.com/mikaoj) for adding keychain synchronization. 252 | * Thanks to [tcirwin](https://github.com/tcirwin) for adding Swift 3.0 support. 253 | * Thanks to [Tulleb](https://github.com/Tulleb) for adding Xcode 8 beta 6 support. 254 | * Thanks to [CraigSiemens](https://github.com/CraigSiemens) for adding Swift 3.1 support. 255 | * Thanks to [maxkramerbcgdv](https://github.com/maxkramerbcgdv) for fixing Package Manager setup in Xcode 8.2. 256 | * Thanks to [elikohen](https://github.com/elikohen) for fixing concurrency issues. 257 | * Thanks to [beny](https://github.com/beny) for adding Swift 4.2 support. 258 | * Thanks to [xuaninbox](https://github.com/xuaninbox) for fixing watchOS deployment target for Xcode 10. 259 | * Thanks to [schayes04](https://github.com/schayes04) for adding Swift 5.0 support. 260 | * Thanks to [mediym41](https://github.com/mediym41) for adding ability to return data as reference. 261 | * Thanks to [AnthonyOliveri](https://github.com/AnthonyOliveri) for adding ability to run unit tests from Swift Package Manager. 262 | * Thanks to [philippec](https://github.com/philippec) for removing deprecated access options. 263 | * Thanks to [lucasmpaim](https://github.com/lucasmpaim) for adding ability to return the names of all keys. 264 | 265 | 266 | 267 | ## Feedback is welcome 268 | 269 | If you notice any issue, got stuck or just want to chat feel free to create an issue. We will be happy to help you. 270 | 271 | ## License 272 | 273 | Keychain Swift is released under the [MIT License](LICENSE). 274 | -------------------------------------------------------------------------------- /Sources/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/KeychainSwift.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #import 4 | 5 | //! Project version number for KeychainSwift. 6 | FOUNDATION_EXPORT double KeychainSwiftVersionNumber; 7 | 8 | //! Project version string for KeychainSwift. 9 | FOUNDATION_EXPORT const unsigned char KeychainSwiftVersionString[]; 10 | 11 | // In this header, you should import all the public headers of your framework using statements like #import 12 | 13 | 14 | -------------------------------------------------------------------------------- /Sources/KeychainSwift.swift: -------------------------------------------------------------------------------- 1 | import Security 2 | import Foundation 3 | 4 | /** 5 | 6 | A collection of helper functions for saving text and data in the keychain. 7 | 8 | */ 9 | open class KeychainSwift { 10 | 11 | var lastQueryParameters: [String: Any]? // Used by the unit tests 12 | 13 | /// Contains result code from the last operation. Value is noErr (0) for a successful result. 14 | open var lastResultCode: OSStatus = noErr 15 | 16 | var keyPrefix = "" // Can be useful in test. 17 | 18 | /** 19 | 20 | Specify an access group that will be used to access keychain items. Access groups can be used to share keychain items between applications. When access group value is nil all application access groups are being accessed. Access group name is used by all functions: set, get, delete and clear. 21 | 22 | */ 23 | open var accessGroup: String? 24 | 25 | 26 | /** 27 | 28 | Specifies whether the items can be synchronized with other devices through iCloud. Setting this property to true will 29 | add the item to other devices with the `set` method and obtain synchronizable items with the `get` command. Deleting synchronizable items will remove them from all devices. In order for keychain synchronization to work the user must enable "Keychain" in iCloud settings. 30 | 31 | Does not work on macOS. 32 | 33 | */ 34 | open var synchronizable: Bool = false 35 | 36 | private let lock = NSLock() 37 | 38 | 39 | /// Instantiate a KeychainSwift object 40 | public init() { } 41 | 42 | /** 43 | 44 | - parameter keyPrefix: a prefix that is added before the key in get/set methods. Note that `clear` method still clears everything from the Keychain. 45 | 46 | */ 47 | public init(keyPrefix: String) { 48 | self.keyPrefix = keyPrefix 49 | } 50 | 51 | /** 52 | 53 | Stores the text value in the keychain item under the given key. 54 | 55 | - parameter key: Key under which the text value is stored in the keychain. 56 | - parameter value: Text string to be written to the keychain. 57 | - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 58 | 59 | - returns: True if the text was successfully written to the keychain. 60 | 61 | */ 62 | @discardableResult 63 | open func set(_ value: String, forKey key: String, 64 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 65 | 66 | if let value = value.data(using: String.Encoding.utf8) { 67 | return set(value, forKey: key, withAccess: access) 68 | } 69 | 70 | return false 71 | } 72 | 73 | /** 74 | 75 | Stores the data in the keychain item under the given key. 76 | 77 | - parameter key: Key under which the data is stored in the keychain. 78 | - parameter value: Data to be written to the keychain. 79 | - parameter withAccess: Value that indicates when your app needs access to the text in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 80 | 81 | - returns: True if the text was successfully written to the keychain. 82 | 83 | */ 84 | @discardableResult 85 | open func set(_ value: Data, forKey key: String, 86 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 87 | 88 | // The lock prevents the code to be run simultaneously 89 | // from multiple threads which may result in crashing 90 | lock.lock() 91 | defer { lock.unlock() } 92 | 93 | deleteNoLock(key) // Delete any existing key before saving it 94 | 95 | let accessible = access?.value ?? KeychainSwiftAccessOptions.defaultOption.value 96 | 97 | let prefixedKey = keyWithPrefix(key) 98 | 99 | var query: [String : Any] = [ 100 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 101 | KeychainSwiftConstants.attrAccount : prefixedKey, 102 | KeychainSwiftConstants.valueData : value, 103 | KeychainSwiftConstants.accessible : accessible 104 | ] 105 | 106 | query = addAccessGroupWhenPresent(query) 107 | query = addSynchronizableIfRequired(query, addingItems: true) 108 | lastQueryParameters = query 109 | 110 | lastResultCode = SecItemAdd(query as CFDictionary, nil) 111 | 112 | return lastResultCode == noErr 113 | } 114 | 115 | /** 116 | 117 | Stores the boolean value in the keychain item under the given key. 118 | 119 | - parameter key: Key under which the value is stored in the keychain. 120 | - parameter value: Boolean to be written to the keychain. 121 | - parameter withAccess: Value that indicates when your app needs access to the value in the keychain item. By default the .AccessibleWhenUnlocked option is used that permits the data to be accessed only while the device is unlocked by the user. 122 | 123 | - returns: True if the value was successfully written to the keychain. 124 | 125 | */ 126 | @discardableResult 127 | open func set(_ value: Bool, forKey key: String, 128 | withAccess access: KeychainSwiftAccessOptions? = nil) -> Bool { 129 | 130 | let bytes: [UInt8] = value ? [1] : [0] 131 | let data = Data(bytes) 132 | 133 | return set(data, forKey: key, withAccess: access) 134 | } 135 | 136 | /** 137 | 138 | Retrieves the text value from the keychain that corresponds to the given key. 139 | 140 | - parameter key: The key that is used to read the keychain item. 141 | - returns: The text value from the keychain. Returns nil if unable to read the item. 142 | 143 | */ 144 | open func get(_ key: String) -> String? { 145 | if let data = getData(key) { 146 | 147 | if let currentString = String(data: data, encoding: .utf8) { 148 | return currentString 149 | } 150 | 151 | lastResultCode = -67853 // errSecInvalidEncoding 152 | } 153 | 154 | return nil 155 | } 156 | 157 | /** 158 | 159 | Retrieves the data from the keychain that corresponds to the given key. 160 | 161 | - parameter key: The key that is used to read the keychain item. 162 | - parameter asReference: If true, returns the data as reference (needed for things like NEVPNProtocol). 163 | - returns: The text value from the keychain. Returns nil if unable to read the item. 164 | 165 | */ 166 | open func getData(_ key: String, asReference: Bool = false) -> Data? { 167 | // The lock prevents the code to be run simultaneously 168 | // from multiple threads which may result in crashing 169 | lock.lock() 170 | defer { lock.unlock() } 171 | 172 | let prefixedKey = keyWithPrefix(key) 173 | 174 | var query: [String: Any] = [ 175 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 176 | KeychainSwiftConstants.attrAccount : prefixedKey, 177 | KeychainSwiftConstants.matchLimit : kSecMatchLimitOne 178 | ] 179 | 180 | if asReference { 181 | query[KeychainSwiftConstants.returnReference] = kCFBooleanTrue 182 | } else { 183 | query[KeychainSwiftConstants.returnData] = kCFBooleanTrue 184 | } 185 | 186 | query = addAccessGroupWhenPresent(query) 187 | query = addSynchronizableIfRequired(query, addingItems: false) 188 | lastQueryParameters = query 189 | 190 | var result: AnyObject? 191 | 192 | lastResultCode = withUnsafeMutablePointer(to: &result) { 193 | SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) 194 | } 195 | 196 | if lastResultCode == noErr { 197 | return result as? Data 198 | } 199 | 200 | return nil 201 | } 202 | 203 | /** 204 | 205 | Retrieves the boolean value from the keychain that corresponds to the given key. 206 | 207 | - parameter key: The key that is used to read the keychain item. 208 | - returns: The boolean value from the keychain. Returns nil if unable to read the item. 209 | 210 | */ 211 | open func getBool(_ key: String) -> Bool? { 212 | guard let data = getData(key) else { return nil } 213 | guard let firstBit = data.first else { return nil } 214 | return firstBit == 1 215 | } 216 | 217 | /** 218 | 219 | Deletes the single keychain item specified by the key. 220 | 221 | - parameter key: The key that is used to delete the keychain item. 222 | - returns: True if the item was successfully deleted. 223 | 224 | */ 225 | @discardableResult 226 | open func delete(_ key: String) -> Bool { 227 | // The lock prevents the code to be run simultaneously 228 | // from multiple threads which may result in crashing 229 | lock.lock() 230 | defer { lock.unlock() } 231 | 232 | return deleteNoLock(key) 233 | } 234 | 235 | /** 236 | Return all keys from keychain 237 | 238 | - returns: An string array with all keys from the keychain. 239 | 240 | */ 241 | public var allKeys: [String] { 242 | var query: [String: Any] = [ 243 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 244 | KeychainSwiftConstants.returnData : true, 245 | KeychainSwiftConstants.returnAttributes: true, 246 | KeychainSwiftConstants.returnReference: true, 247 | KeychainSwiftConstants.matchLimit: KeychainSwiftConstants.secMatchLimitAll 248 | ] 249 | 250 | query = addAccessGroupWhenPresent(query) 251 | query = addSynchronizableIfRequired(query, addingItems: false) 252 | 253 | var result: AnyObject? 254 | 255 | let lastResultCode = withUnsafeMutablePointer(to: &result) { 256 | SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) 257 | } 258 | 259 | if lastResultCode == noErr { 260 | return (result as? [[String: Any]])?.compactMap { 261 | $0[KeychainSwiftConstants.attrAccount] as? String } ?? [] 262 | } 263 | 264 | return [] 265 | } 266 | 267 | /** 268 | 269 | Same as `delete` but is only accessed internally, since it is not thread safe. 270 | 271 | - parameter key: The key that is used to delete the keychain item. 272 | - returns: True if the item was successfully deleted. 273 | 274 | */ 275 | @discardableResult 276 | func deleteNoLock(_ key: String) -> Bool { 277 | let prefixedKey = keyWithPrefix(key) 278 | 279 | var query: [String: Any] = [ 280 | KeychainSwiftConstants.klass : kSecClassGenericPassword, 281 | KeychainSwiftConstants.attrAccount : prefixedKey 282 | ] 283 | 284 | query = addAccessGroupWhenPresent(query) 285 | query = addSynchronizableIfRequired(query, addingItems: false) 286 | lastQueryParameters = query 287 | 288 | lastResultCode = SecItemDelete(query as CFDictionary) 289 | 290 | return lastResultCode == noErr 291 | } 292 | 293 | /** 294 | 295 | Deletes all Keychain items used by the app. Note that this method deletes all items regardless of the prefix settings used for initializing the class. 296 | 297 | - returns: True if the keychain items were successfully deleted. 298 | 299 | */ 300 | @discardableResult 301 | open func clear() -> Bool { 302 | // The lock prevents the code to be run simultaneously 303 | // from multiple threads which may result in crashing 304 | lock.lock() 305 | defer { lock.unlock() } 306 | 307 | var query: [String: Any] = [ kSecClass as String : kSecClassGenericPassword ] 308 | query = addAccessGroupWhenPresent(query) 309 | query = addSynchronizableIfRequired(query, addingItems: false) 310 | lastQueryParameters = query 311 | 312 | lastResultCode = SecItemDelete(query as CFDictionary) 313 | 314 | return lastResultCode == noErr 315 | } 316 | 317 | /// Returns the key with currently set prefix. 318 | func keyWithPrefix(_ key: String) -> String { 319 | return "\(keyPrefix)\(key)" 320 | } 321 | 322 | func addAccessGroupWhenPresent(_ items: [String: Any]) -> [String: Any] { 323 | guard let accessGroup = accessGroup else { return items } 324 | 325 | var result: [String: Any] = items 326 | result[KeychainSwiftConstants.accessGroup] = accessGroup 327 | return result 328 | } 329 | 330 | /** 331 | 332 | Adds kSecAttrSynchronizable: kSecAttrSynchronizableAny` item to the dictionary when the `synchronizable` property is true. 333 | 334 | - parameter items: The dictionary where the kSecAttrSynchronizable items will be added when requested. 335 | - parameter addingItems: Use `true` when the dictionary will be used with `SecItemAdd` method (adding a keychain item). For getting and deleting items, use `false`. 336 | 337 | - returns: the dictionary with kSecAttrSynchronizable item added if it was requested. Otherwise, it returns the original dictionary. 338 | 339 | */ 340 | func addSynchronizableIfRequired(_ items: [String: Any], addingItems: Bool) -> [String: Any] { 341 | if !synchronizable { return items } 342 | var result: [String: Any] = items 343 | result[KeychainSwiftConstants.attrSynchronizable] = addingItems == true ? true : kSecAttrSynchronizableAny 344 | return result 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /Sources/KeychainSwiftAccessOptions.swift: -------------------------------------------------------------------------------- 1 | import Security 2 | 3 | /** 4 | 5 | These options are used to determine when a keychain item should be readable. The default value is AccessibleWhenUnlocked. 6 | 7 | */ 8 | public enum KeychainSwiftAccessOptions { 9 | 10 | /** 11 | 12 | The data in the keychain item can be accessed only while the device is unlocked by the user. 13 | 14 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups. 15 | 16 | This is the default value for keychain items added without explicitly setting an accessibility constant. 17 | 18 | */ 19 | case accessibleWhenUnlocked 20 | 21 | /** 22 | 23 | The data in the keychain item can be accessed only while the device is unlocked by the user. 24 | 25 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. 26 | 27 | */ 28 | case accessibleWhenUnlockedThisDeviceOnly 29 | 30 | /** 31 | 32 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. 33 | 34 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups. 35 | 36 | */ 37 | case accessibleAfterFirstUnlock 38 | 39 | /** 40 | 41 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. 42 | 43 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present. 44 | 45 | */ 46 | case accessibleAfterFirstUnlockThisDeviceOnly 47 | 48 | /** 49 | 50 | The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device. 51 | 52 | This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted. 53 | 54 | */ 55 | case accessibleWhenPasscodeSetThisDeviceOnly 56 | 57 | static var defaultOption: KeychainSwiftAccessOptions { 58 | return .accessibleWhenUnlocked 59 | } 60 | 61 | var value: String { 62 | switch self { 63 | case .accessibleWhenUnlocked: 64 | return toString(kSecAttrAccessibleWhenUnlocked) 65 | 66 | case .accessibleWhenUnlockedThisDeviceOnly: 67 | return toString(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) 68 | 69 | case .accessibleAfterFirstUnlock: 70 | return toString(kSecAttrAccessibleAfterFirstUnlock) 71 | 72 | case .accessibleAfterFirstUnlockThisDeviceOnly: 73 | return toString(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) 74 | 75 | case .accessibleWhenPasscodeSetThisDeviceOnly: 76 | return toString(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) 77 | } 78 | } 79 | 80 | func toString(_ value: CFString) -> String { 81 | return KeychainSwiftConstants.toString(value) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyCollectedDataTypes 6 | 7 | NSPrivacyAccessedAPITypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyTracking 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/TegKeychainConstants.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Security 3 | 4 | /// Constants used by the library 5 | public struct KeychainSwiftConstants { 6 | /// Specifies a Keychain access group. Used for sharing Keychain items between apps. 7 | public static var accessGroup: String { return toString(kSecAttrAccessGroup) } 8 | 9 | /** 10 | 11 | A value that indicates when your app needs access to the data in a keychain item. The default value is AccessibleWhenUnlocked. For a list of possible values, see KeychainSwiftAccessOptions. 12 | 13 | */ 14 | public static var accessible: String { return toString(kSecAttrAccessible) } 15 | 16 | /// Used for specifying a String key when setting/getting a Keychain value. 17 | public static var attrAccount: String { return toString(kSecAttrAccount) } 18 | 19 | /// Used for specifying synchronization of keychain items between devices. 20 | public static var attrSynchronizable: String { return toString(kSecAttrSynchronizable) } 21 | 22 | /// An item class key used to construct a Keychain search dictionary. 23 | public static var klass: String { return toString(kSecClass) } 24 | 25 | /// Specifies the number of values returned from the keychain. The library only supports single values. 26 | public static var matchLimit: String { return toString(kSecMatchLimit) } 27 | 28 | /// A return data type used to get the data from the Keychain. 29 | public static var returnData: String { return toString(kSecReturnData) } 30 | 31 | /// Used for specifying a value when setting a Keychain value. 32 | public static var valueData: String { return toString(kSecValueData) } 33 | 34 | /// Used for returning a reference to the data from the keychain 35 | public static var returnReference: String { return toString(kSecReturnPersistentRef) } 36 | 37 | /// A key whose value is a Boolean indicating whether or not to return item attributes 38 | public static var returnAttributes : String { return toString(kSecReturnAttributes) } 39 | 40 | /// A value that corresponds to matching an unlimited number of items 41 | public static var secMatchLimitAll : String { return toString(kSecMatchLimitAll) } 42 | 43 | static func toString(_ value: CFString) -> String { 44 | return value as String 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/AccessGroupTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class AccessGroupTests: XCTestCase { 5 | 6 | var obj: KeychainSwift! 7 | 8 | override func setUp() { 9 | super.setUp() 10 | 11 | obj = KeychainSwift() 12 | obj.clear() 13 | obj.lastQueryParameters = nil 14 | obj.accessGroup = nil 15 | } 16 | 17 | // MARK: - Add access group 18 | 19 | func testAddAccessGroup() { 20 | let items: [String: Any] = [ 21 | "one": "two" 22 | ] 23 | 24 | obj.accessGroup = "123.my.test.group" 25 | let result = obj.addAccessGroupWhenPresent(items) 26 | 27 | XCTAssertEqual(2, result.count) 28 | XCTAssertEqual("two", result["one"] as! String) 29 | XCTAssertEqual("123.my.test.group", result["agrp"] as! String) 30 | } 31 | 32 | func testAddAccessGroup_nil() { 33 | let items: [String: Any] = [ 34 | "one": "two" 35 | ] 36 | 37 | let result = obj.addAccessGroupWhenPresent(items) 38 | 39 | XCTAssertEqual(1, result.count) 40 | XCTAssertEqual("two", result["one"] as! String) 41 | } 42 | 43 | func testSet() { 44 | obj.accessGroup = "123.my.test.group" 45 | obj.set("hello :)", forKey: "key 1") 46 | XCTAssertEqual("123.my.test.group", obj.lastQueryParameters?["agrp"] as! String) 47 | } 48 | 49 | func testGet() { 50 | obj.accessGroup = "123.my.test.group" 51 | _ = obj.get("key 1") 52 | XCTAssertEqual("123.my.test.group", obj.lastQueryParameters?["agrp"] as! String) 53 | } 54 | 55 | func testDelete() { 56 | obj.accessGroup = "123.my.test.group" 57 | obj.delete("key 1") 58 | XCTAssertEqual("123.my.test.group", obj.lastQueryParameters?["agrp"] as! String) 59 | } 60 | 61 | func testClear() { 62 | obj.accessGroup = "123.my.test.group" 63 | obj.clear() 64 | XCTAssertEqual("123.my.test.group", obj.lastQueryParameters?["agrp"] as! String) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/AllKeysTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AllKeysTests.swift 3 | // KeychainSwiftTests 4 | // 5 | // Created by Lucas Paim on 02/01/20. 6 | // Copyright © 2020 Evgenii Neumerzhitckii. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import KeychainSwift 11 | 12 | 13 | class AllKeysTests: XCTestCase { 14 | 15 | var obj: KeychainSwift! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | obj = KeychainSwift() 21 | obj.clear() 22 | } 23 | 24 | // MARK: - allKeys 25 | func testAddSynchronizableGroup_addItemsFalse() { 26 | let items: [String] = [ 27 | "one", "two" 28 | ] 29 | 30 | items.enumerated().forEach { enumerator in 31 | self.obj!.set("\(enumerator.offset)", forKey: enumerator.element) 32 | } 33 | 34 | XCTAssertEqual(["one", "two"], obj.allKeys) 35 | 36 | obj.clear() 37 | XCTAssertEqual(obj.allKeys, []) 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/ClearTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class ClearTests: XCTestCase { 5 | 6 | var obj: KeychainSwift! 7 | 8 | override func setUp() { 9 | super.setUp() 10 | 11 | obj = KeychainSwift() 12 | } 13 | 14 | func testClear() { 15 | obj.set("hello :)", forKey: "key 1") 16 | obj.set("hello two", forKey: "key 2") 17 | 18 | obj.clear() 19 | 20 | XCTAssert(obj.get("key 1") == nil) 21 | XCTAssert(obj.get("key 2") == nil) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/ConcurrencyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConcurrencyTests.swift 3 | // KeychainSwift 4 | // 5 | // Created by Eli Kohen on 08/02/2017. 6 | // 7 | 8 | import XCTest 9 | @testable import KeychainSwift 10 | 11 | class ConcurrencyTests: XCTestCase { 12 | 13 | var obj: KeychainSwift! 14 | 15 | override func setUp() { 16 | super.setUp() 17 | 18 | obj = KeychainSwift() 19 | obj.clear() 20 | obj.lastQueryParameters = nil 21 | obj.synchronizable = false 22 | } 23 | 24 | // MARK: - addSynchronizableIfRequired 25 | 26 | func testConcurrencyDoesntCrash() { 27 | 28 | let expectation = self.expectation(description: "Wait for write loop") 29 | let expectation2 = self.expectation(description: "Wait for write loop") 30 | 31 | 32 | let dataToWrite = "{ asdf ñlk BNALSKDJFÑLAKSJDFÑLKJ ZÑCLXKJ ÑALSKDFJÑLKASJDFÑLKJASDÑFLKJAÑSDLKFJÑLKJ}" 33 | obj.set(dataToWrite, forKey: "test-key") 34 | 35 | var writes = 0 36 | 37 | let readQueue = DispatchQueue(label: "ReadQueue", attributes: []) 38 | readQueue.async { 39 | for _ in 0..<400 { 40 | let _: String? = synchronize( { completion in 41 | let result: String? = self.obj.get("test-key") 42 | DispatchQueue.global(qos: .background).async { 43 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 44 | completion(result) 45 | } 46 | } 47 | }, timeoutWith: nil) 48 | } 49 | } 50 | let readQueue2 = DispatchQueue(label: "ReadQueue2", attributes: []) 51 | readQueue2.async { 52 | for _ in 0..<400 { 53 | let _: String? = synchronize( { completion in 54 | let result: String? = self.obj.get("test-key") 55 | DispatchQueue.global(qos: .background).async { 56 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 57 | completion(result) 58 | } 59 | } 60 | }, timeoutWith: nil) 61 | } 62 | } 63 | let readQueue3 = DispatchQueue(label: "ReadQueue3", attributes: []) 64 | readQueue3.async { 65 | for _ in 0..<400 { 66 | let _: String? = synchronize( { completion in 67 | let result: String? = self.obj.get("test-key") 68 | DispatchQueue.global(qos: .background).async { 69 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 70 | completion(result) 71 | } 72 | } 73 | }, timeoutWith: nil) 74 | } 75 | } 76 | 77 | let deleteQueue = DispatchQueue(label: "deleteQueue", attributes: []) 78 | deleteQueue.async { 79 | for _ in 0..<400 { 80 | let _: Bool = synchronize( { completion in 81 | let result = self.obj.delete("test-key") 82 | DispatchQueue.global(qos: .background).async { 83 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 84 | completion(result) 85 | } 86 | } 87 | }, timeoutWith: false) 88 | } 89 | } 90 | 91 | let deleteQueue2 = DispatchQueue(label: "deleteQueue2", attributes: []) 92 | deleteQueue2.async { 93 | for _ in 0..<400 { 94 | let _: Bool = synchronize( { completion in 95 | let result = self.obj.delete("test-key") 96 | DispatchQueue.global(qos: .background).async { 97 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 98 | completion(result) 99 | } 100 | } 101 | }, timeoutWith: false) 102 | } 103 | } 104 | 105 | let clearQueue = DispatchQueue(label: "clearQueue", attributes: []) 106 | clearQueue.async { 107 | for _ in 0..<400 { 108 | let _: Bool = synchronize( { completion in 109 | let result = self.obj.clear() 110 | DispatchQueue.global(qos: .background).async { 111 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 112 | completion(result) 113 | } 114 | } 115 | }, timeoutWith: false) 116 | } 117 | } 118 | 119 | let clearQueue2 = DispatchQueue(label: "clearQueue2", attributes: []) 120 | clearQueue2.async { 121 | for _ in 0..<400 { 122 | let _: Bool = synchronize( { completion in 123 | let result = self.obj.clear() 124 | DispatchQueue.global(qos: .background).async { 125 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 126 | completion(result) 127 | } 128 | } 129 | }, timeoutWith: false) 130 | } 131 | } 132 | 133 | let writeQueue = DispatchQueue(label: "WriteQueue", attributes: []) 134 | writeQueue.async { 135 | for _ in 0..<500 { 136 | let written: Bool = synchronize({ completion in 137 | DispatchQueue.global(qos: .background).async { 138 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 139 | let result = self.obj.set(dataToWrite, forKey: "test-key") 140 | completion(result) 141 | } 142 | } 143 | }, timeoutWith: false) 144 | if written { 145 | writes = writes + 1 146 | } 147 | } 148 | expectation.fulfill() 149 | } 150 | 151 | let writeQueue2 = DispatchQueue(label: "WriteQueue2", attributes: []) 152 | writeQueue2.async { 153 | for _ in 0..<500 { 154 | let written: Bool = synchronize({ completion in 155 | DispatchQueue.global(qos: .background).async { 156 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) { 157 | let result = self.obj.set(dataToWrite, forKey: "test-key") 158 | completion(result) 159 | } 160 | } 161 | }, timeoutWith: false) 162 | if written { 163 | writes = writes + 1 164 | } 165 | } 166 | expectation2.fulfill() 167 | } 168 | 169 | for _ in 0..<1000 { 170 | self.obj.set(dataToWrite, forKey: "test-key") 171 | let _ = self.obj.get("test-key") 172 | } 173 | self.waitForExpectations(timeout: 30, handler: nil) 174 | 175 | XCTAssertEqual(1000, writes) 176 | } 177 | } 178 | 179 | 180 | // Synchronizes a asynch closure 181 | // Ref: https://forums.developer.apple.com/thread/11519 182 | func synchronize(_ asynchClosure: (_ completion: @escaping (ResultType) -> ()) -> Void, 183 | timeout: DispatchTime = DispatchTime.distantFuture, timeoutWith: @autoclosure @escaping () -> ResultType) -> ResultType { 184 | let sem = DispatchSemaphore(value: 0) 185 | 186 | var result: ResultType? 187 | 188 | asynchClosure { (r: ResultType) -> () in 189 | result = r 190 | sem.signal() 191 | } 192 | _ = sem.wait(timeout: timeout) 193 | if result == nil { 194 | result = timeoutWith() 195 | } 196 | return result! 197 | } 198 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/KeychainSwiftPrefixedTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class KeychainWithPrefixTests: XCTestCase { 5 | 6 | var prefixed: KeychainSwift! 7 | var nonPrefixed: KeychainSwift! 8 | 9 | 10 | override func setUp() { 11 | super.setUp() 12 | 13 | prefixed = KeychainSwift(keyPrefix: "test_prefix_") 14 | nonPrefixed = KeychainSwift() 15 | 16 | prefixed.clear() 17 | nonPrefixed.clear() 18 | 19 | prefixed.lastQueryParameters = nil 20 | } 21 | 22 | func testKeyWithPrefix() { 23 | XCTAssertEqual("test_prefix_key", prefixed.keyWithPrefix("key")) 24 | XCTAssertEqual("key", nonPrefixed.keyWithPrefix("key")) 25 | } 26 | 27 | // MARK: - Set text 28 | // ----------------------- 29 | 30 | func testSet() { 31 | let key = "key 1" 32 | XCTAssertTrue(prefixed.set("prefixed", forKey: key)) 33 | XCTAssertTrue(nonPrefixed.set("non prefixed", forKey: key)) 34 | 35 | XCTAssertEqual("prefixed", prefixed.get(key)!) 36 | XCTAssertEqual("non prefixed", nonPrefixed.get(key)!) 37 | } 38 | 39 | 40 | // MARK: - Set data 41 | // ----------------------- 42 | 43 | func testSetData() { 44 | let key = "key 123" 45 | 46 | let dataPrefixed = "prefixed".data(using: String.Encoding.utf8)! 47 | let dataNonPrefixed = "non prefixed".data(using: String.Encoding.utf8)! 48 | 49 | XCTAssertTrue(prefixed.set(dataPrefixed, forKey: key)) 50 | XCTAssertTrue(nonPrefixed.set(dataNonPrefixed, forKey: key)) 51 | 52 | 53 | let dataFromKeychainPrefixed = prefixed.getData(key)! 54 | let textFromKeychainPrefixed = String(data: dataFromKeychainPrefixed, encoding: .utf8)! 55 | XCTAssertEqual("prefixed", textFromKeychainPrefixed) 56 | 57 | let dataFromKeychainNonPrefixed = nonPrefixed.getData(key)! 58 | let textFromKeychainNonPrefixed = String(data: dataFromKeychainNonPrefixed, encoding: .utf8)! 59 | XCTAssertEqual("non prefixed", textFromKeychainNonPrefixed) 60 | } 61 | 62 | // MARK: - Delete 63 | // ----------------------- 64 | 65 | func testDelete() { 66 | let key = "key 1" 67 | XCTAssertTrue(prefixed.set("prefixed", forKey: key)) 68 | XCTAssertTrue(nonPrefixed.set("non prefixed", forKey: key)) 69 | 70 | prefixed.delete(key) 71 | 72 | XCTAssert(prefixed.get(key) == nil) 73 | XCTAssertFalse(nonPrefixed.get(key) == nil) // non-prefixed still exists 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/KeychainSwiftTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/KeychainSwiftTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class KeychainSwiftTests: XCTestCase { 5 | 6 | var obj: KeychainSwift! 7 | 8 | override func setUp() { 9 | super.setUp() 10 | 11 | obj = KeychainSwift() 12 | obj.clear() 13 | obj.lastQueryParameters = nil 14 | } 15 | 16 | // MARK: - Set text 17 | // ----------------------- 18 | 19 | func testSet() { 20 | XCTAssertTrue(obj.set("hello :)", forKey: "key 1")) 21 | XCTAssertEqual("hello :)", obj.get("key 1")!) 22 | } 23 | 24 | func testSet_usesAccessibleWhenUnlockedByDefault() { 25 | XCTAssertTrue(obj.set("hello :)", forKey: "key 1")) 26 | 27 | let accessValue = obj.lastQueryParameters?[KeychainSwiftConstants.accessible] as? String 28 | XCTAssertEqual(KeychainSwiftAccessOptions.accessibleWhenUnlocked.value, accessValue!) 29 | } 30 | 31 | func testSetWithAccessOption() { 32 | obj.set("hello :)", forKey: "key 1", withAccess: .accessibleAfterFirstUnlock) 33 | let accessValue = obj.lastQueryParameters?[KeychainSwiftConstants.accessible] as? String 34 | XCTAssertEqual(KeychainSwiftAccessOptions.accessibleAfterFirstUnlock.value, accessValue!) 35 | } 36 | 37 | // MARK: - Set data 38 | // ----------------------- 39 | 40 | func testSetData() { 41 | let data = "hello world".data(using: String.Encoding.utf8)! 42 | 43 | XCTAssertTrue(obj.set(data, forKey: "key 123")) 44 | 45 | let dataFromKeychain = obj.getData("key 123")! 46 | let textFromKeychain = String(data: dataFromKeychain, encoding:String.Encoding.utf8)! 47 | XCTAssertEqual("hello world", textFromKeychain) 48 | } 49 | 50 | func testSetData_usesAccessibleWhenUnlockedByDefault() { 51 | let data = "hello world".data(using: String.Encoding.utf8)! 52 | 53 | obj.set(data, forKey: "key 123") 54 | 55 | let accessValue = obj.lastQueryParameters?[KeychainSwiftConstants.accessible] as? String 56 | XCTAssertEqual(KeychainSwiftAccessOptions.accessibleWhenUnlocked.value, accessValue!) 57 | } 58 | 59 | // MARK: - Set bool 60 | // ----------------------- 61 | 62 | func testSetBool() { 63 | XCTAssertTrue(obj.set(true, forKey: "key bool")) 64 | XCTAssertTrue(obj.getBool("key bool")!) 65 | XCTAssertTrue(obj.set(false, forKey: "key bool")) 66 | XCTAssertFalse(obj.getBool("key bool")!) 67 | } 68 | 69 | func testSetBool_usesAccessibleWhenUnlockedByDefault() { 70 | XCTAssertTrue(obj.set(false, forKey: "key bool")) 71 | let accessValue = obj.lastQueryParameters?[KeychainSwiftConstants.accessible] as? String 72 | XCTAssertEqual(KeychainSwiftAccessOptions.accessibleWhenUnlocked.value, accessValue!) 73 | } 74 | 75 | // MARK: - Get 76 | // ----------------------- 77 | 78 | func testGet_returnNilWhenValueNotSet() { 79 | XCTAssert(obj.get("key 1") == nil) 80 | } 81 | 82 | // MARK: - Get bool 83 | // ----------------------- 84 | 85 | func testGetBool_returnNilWhenValueNotSet() { 86 | XCTAssert(obj.getBool("some bool key") == nil) 87 | } 88 | 89 | // MARK: - Delete 90 | // ----------------------- 91 | 92 | func testDelete() { 93 | obj.set("hello :)", forKey: "key 1") 94 | obj.delete("key 1") 95 | 96 | XCTAssert(obj.get("key 1") == nil) 97 | } 98 | 99 | func testDelete_deleteOnSingleKey() { 100 | obj.set("hello :)", forKey: "key 1") 101 | obj.set("hello two", forKey: "key 2") 102 | 103 | obj.delete("key 1") 104 | 105 | XCTAssertEqual("hello two", obj.get("key 2")!) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/SynchronizableTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class SynchronizableTests: XCTestCase { 5 | 6 | var obj: KeychainSwift! 7 | 8 | override func setUp() { 9 | super.setUp() 10 | 11 | obj = KeychainSwift() 12 | obj.clear() 13 | obj.lastQueryParameters = nil 14 | obj.synchronizable = false 15 | } 16 | 17 | // MARK: - addSynchronizableIfRequired 18 | 19 | func testAddSynchronizableGroup_addItemsFalse() { 20 | let items: [String: Any] = [ 21 | "one": "two" 22 | ] 23 | 24 | obj.synchronizable = true 25 | let result = obj.addSynchronizableIfRequired(items, addingItems: false) 26 | 27 | XCTAssertEqual(2, result.count) 28 | XCTAssertEqual("two", result["one"] as! String) 29 | XCTAssertEqual(kSecAttrSynchronizableAny as String, result["sync"] as! String) 30 | } 31 | 32 | func testAddSynchronizableGroup_addItemsTrue() { 33 | let items: [String: Any] = [ 34 | "one": "two" 35 | ] 36 | 37 | obj.synchronizable = true 38 | let result = obj.addSynchronizableIfRequired(items, addingItems: true) 39 | 40 | XCTAssertEqual(2, result.count) 41 | XCTAssertEqual("two", result["one"] as! String) 42 | XCTAssertEqual(true, result["sync"] as! Bool) 43 | } 44 | 45 | func testAddSynchronizableGroup_nil() { 46 | let items: [String: Any] = [ 47 | "one": "two" 48 | ] 49 | 50 | let result = obj.addSynchronizableIfRequired(items, addingItems: false) 51 | 52 | XCTAssertEqual(1, result.count) 53 | XCTAssertEqual("two", result["one"] as! String) 54 | } 55 | 56 | // MARK: - Set 57 | 58 | func testSet() { 59 | obj.synchronizable = true 60 | obj.set("hello :)", forKey: "key 1") 61 | XCTAssertEqual(true, obj.lastQueryParameters?["sync"] as! Bool) 62 | } 63 | 64 | func testSet_doNotSetSynchronizable() { 65 | obj.set("hello :)", forKey: "key 1") 66 | XCTAssertNil(obj.lastQueryParameters?["sync"]) 67 | } 68 | 69 | // MARK: - Get 70 | 71 | func testGet() { 72 | obj.synchronizable = true 73 | _ = obj.get("key 1") 74 | XCTAssertEqual(kSecAttrSynchronizableAny as String, obj.lastQueryParameters?["sync"] as! String) 75 | } 76 | 77 | func testGet_doNotSetSynchronizable() { 78 | _ = obj.get("key 1") 79 | XCTAssertNil(obj.lastQueryParameters?["sync"]) 80 | } 81 | 82 | // MARK: - Delete 83 | 84 | func testDelete() { 85 | obj.synchronizable = true 86 | obj.delete("key 1") 87 | XCTAssertEqual(kSecAttrSynchronizableAny as String, obj.lastQueryParameters?["sync"] as! String) 88 | } 89 | 90 | func testDelete_doNotSetSynchronizable() { 91 | obj.delete("key 1") 92 | XCTAssertNil(obj.lastQueryParameters?["sync"]) 93 | } 94 | 95 | // MARK: - Clear 96 | 97 | func testClear() { 98 | obj.synchronizable = true 99 | obj.clear() 100 | XCTAssertEqual(kSecAttrSynchronizableAny as String, obj.lastQueryParameters?["sync"] as! String) 101 | } 102 | 103 | func testClear_doNotSetSynchronizable() { 104 | obj.clear() 105 | XCTAssertNil(obj.lastQueryParameters?["sync"]) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/macOS Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/KeychainSwiftTests/macOS Tests/macOS_Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KeychainSwift 3 | 4 | class macOS_Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | } 9 | 10 | override func tearDown() { 11 | super.tearDown() 12 | } 13 | 14 | func testExample() { 15 | XCTAssertTrue(true) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /graphics/keychain-swift-demo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evgenyneu/keychain-swift/5e1b02b6a9dac2a759a1d5dbc175c86bd192a608/graphics/keychain-swift-demo-3.png -------------------------------------------------------------------------------- /graphics/keychain_swift_video_tutorial.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evgenyneu/keychain-swift/5e1b02b6a9dac2a759a1d5dbc175c86bd192a608/graphics/keychain_swift_video_tutorial.jpg -------------------------------------------------------------------------------- /macOS Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | @NSApplicationMain 4 | class AppDelegate: NSObject, NSApplicationDelegate { 5 | 6 | 7 | 8 | func applicationDidFinishLaunching(_ aNotification: Notification) { 9 | // Insert code here to initialize your application 10 | } 11 | 12 | func applicationWillTerminate(_ aNotification: Notification) { 13 | // Insert code here to tear down your application 14 | } 15 | 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /macOS Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /macOS Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 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 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2016 Evgenii Neumerzhitckii. No rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macOS Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import KeychainSwift 3 | 4 | let TegKeychainDemo_keyName = "my key" 5 | 6 | class ViewController: NSViewController { 7 | 8 | @IBOutlet weak var textField: NSTextField! 9 | 10 | @IBOutlet weak var valueLabel: NSTextField! 11 | 12 | @IBOutlet weak var errorLabel: NSTextField! 13 | 14 | @IBOutlet weak var synchronizableButton: NSButton! 15 | 16 | let keychain = KeychainSwift() 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | updateValueLabel() 22 | errorLabel.stringValue = "" 23 | } 24 | 25 | // override var representedObject: Any? { 26 | // didSet { 27 | // // Update the view, if already loaded. 28 | // } 29 | // } 30 | 31 | @IBAction func onSaveTapped(_ sender: AnyObject) { 32 | keychain.synchronizable = synchronizableButton.state == NSControl.StateValue.on 33 | keychain.set(textField.stringValue, forKey: TegKeychainDemo_keyName) 34 | errorLabel.stringValue = "Result code: \(keychain.lastResultCode)" 35 | updateValueLabel() 36 | } 37 | 38 | 39 | @IBAction func onDeleteTapped(_ sender: AnyObject) { 40 | keychain.synchronizable = synchronizableButton.state == NSControl.StateValue.on 41 | keychain.delete(TegKeychainDemo_keyName) 42 | errorLabel.stringValue = "Result code: \(keychain.lastResultCode)" 43 | updateValueLabel() 44 | } 45 | 46 | @IBAction func onGetTapped(_ sender: AnyObject) { 47 | updateValueLabel() 48 | } 49 | 50 | private func updateValueLabel() { 51 | keychain.synchronizable = synchronizableButton.state == NSControl.StateValue.on 52 | 53 | if let value = keychain.get(TegKeychainDemo_keyName) { 54 | valueLabel.stringValue = "In Keychain: \(value)" 55 | } else { 56 | valueLabel.stringValue = "no value in keychain" 57 | } 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /scripts/concatenate_swift_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Combines *.swift files into a single file. Used in Xcode to build a single swift distributive file. 5 | # 6 | # Here is how to use it in Xcode: 7 | # 8 | # 1. Create an "External build system" target. 9 | # 2. Click "Info" tab in target settings. 10 | # 3. In "Build Tool" field specify the path to this script file, for example: $PROJECT_DIR/scripts/concatenate_swift_files.sh 11 | # 4. In "Arguments" field specify the arguments, for example $PROJECT_DIR/YourSubDir $PROJECT_DIR/Distrib/Distrib.swift "// Your header" 12 | # 5. Build the target and it will concatenate your swift files into a single swift file. 13 | # 14 | # You can see an example of using the script in this project: https://github.com/evgenyneu/moa 15 | # 16 | # Usage 17 | # ------ 18 | # 19 | # ./combine_swift_files.sh source_dir destination_file [optional_header_text] [remove_line_text] 20 | # 21 | # 22 | # Example 23 | # -------- 24 | # 25 | # Use in external build tool in Xcode. 26 | # 27 | # Build tool: 28 | # 29 | # $PROJECT_DIR/scripts/concatenate_swift_files.sh 30 | # 31 | # Arguments: 32 | # 33 | # $PROJECT_DIR/MyProject $PROJECT_DIR/Distrib/MyDistrib.swift "// My header" "remove this line" 34 | # 35 | 36 | # Handle paths with spaces (http://unix.stackexchange.com/a/9499) 37 | IFS=$'\n' 38 | 39 | destination=$2 40 | headermessage=$3 41 | remove_text=$4 42 | 43 | if [ "$#" -lt 2 ] 44 | then 45 | echo "\nUsage:\n" 46 | echo " ./combine_swift_files.sh source_dir destination_file [optional_header_text] [remove text]\n" 47 | exit 1 48 | fi 49 | 50 | # Create empty destination file 51 | echo > "$destination"; 52 | text="" 53 | destination_filename=$(basename "$destination") 54 | 55 | for swift in `find $1 ! -name "$destination_filename" -name "*.swift"`; 56 | do 57 | filename=$(basename "$swift") 58 | 59 | text="$text\n// ----------------------------"; 60 | text="$text\n//"; 61 | text="$text\n// ${filename}"; 62 | text="$text\n//"; 63 | text="$text\n// ----------------------------\n\n"; 64 | 65 | if [ -n "$remove_text" ] 66 | then 67 | filecontent="$(cat "${swift}"| sed "/${remove_text}/d";)" 68 | else 69 | filecontent="$(cat "${swift}";)" 70 | fi 71 | 72 | text="$text$filecontent\n\n"; 73 | 74 | echo "Combining $swift"; 75 | done; 76 | 77 | # Add header message 78 | if [ -n "$headermessage" ] 79 | then 80 | text="$headermessage\n\n$text" 81 | fi 82 | 83 | # Write to destination file 84 | echo -e "$text" > "$destination" 85 | 86 | echo -e "\nSwift files combined into $destination" 87 | 88 | --------------------------------------------------------------------------------