├── .swift-version
├── TestHostAppTvOS
├── Assets.xcassets
│ ├── Contents.json
│ ├── App Icon & Top Shelf Image.brandassets
│ │ ├── App Icon.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ ├── Middle.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── App Icon - App Store.imagestack
│ │ │ ├── Back.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ ├── Front.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ ├── Middle.imagestacklayer
│ │ │ │ ├── Contents.json
│ │ │ │ └── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Top Shelf Image.imageset
│ │ │ └── Contents.json
│ │ ├── Top Shelf Image Wide.imageset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── ViewController.swift
├── Info.plist
├── AppDelegate.swift
└── Base.lproj
│ └── Main.storyboard
├── .travis.yml
├── SwiftKeychainWrapper.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── SwiftKeychainWrapperTests.xcscheme
│ ├── SwiftKeychainWrapperOSX.xcscheme
│ ├── SwiftKeychainWrapperTvOS.xcscheme
│ ├── TestHostAppTvOS.xcscheme
│ ├── TestHostAppOSX.xcscheme
│ ├── SwiftKeychainWrapper.xcscheme
│ └── TestHostApp.xcscheme
├── Debug.entitlements
├── TestHostApp
├── Debug.entitlements
├── ViewController.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
└── AppDelegate.swift
├── TestHostAppOSX
├── TestHostAppOSX.entitlements
├── ViewController.swift
├── AppDelegate.swift
├── Info.plist
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
└── Base.lproj
│ └── Main.storyboard
├── .gitignore
├── SwiftKeychainWrapperOSX
├── SwiftKeychainWrapperOSX.h
└── Info.plist
├── SwiftKeychainWrapperTvOS
├── SwiftKeychainWrapperTvOS.h
└── Info.plist
├── TestHostAppOSXTests
├── Info.plist
└── TestHostAppOSXTests.swift
├── TestHostAppTvOSTests
├── Info.plist
└── TestHostAppTvOSTests.swift
├── SwiftKeychainWrapper.podspec
├── SwiftKeychainWrapperTests
├── Info.plist
├── TestObject.swift
├── KeychainWrapperDeleteTests.swift
├── KeychainWrapperTests.swift
├── KeychainWrapperPrimitiveValueTests.swift
└── KeychainWrapperDefaultWrapperTests.swift
├── SwiftKeychainWrapper
├── Info.plist
├── SwiftKeychainWrapper.h
├── KeychainItemAccessibility.swift
└── KeychainWrapper.swift
├── LICENSE
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode9
3 |
4 | script:
5 | - xcodebuild test -scheme SwiftKeychainWrapper -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone SE,OS=11.0"
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Debug.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | get-task-allow
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TestHostApp/Debug.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | get-task-allow
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "version" : 1,
9 | "author" : "xcode"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "version" : 1,
9 | "author" : "xcode"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "version" : 1,
9 | "author" : "xcode"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "landscape",
5 | "idiom" : "tv",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "9.0",
8 | "scale" : "1x"
9 | }
10 | ],
11 | "info" : {
12 | "version" : 1,
13 | "author" : "xcode"
14 | }
15 | }
--------------------------------------------------------------------------------
/TestHostAppOSX/TestHostAppOSX.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/.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 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | # Pods/
27 |
--------------------------------------------------------------------------------
/TestHostAppOSX/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // TestHostAppOSX
4 | //
5 | // Created by Anton Bukarev on 30/11/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | override var representedObject: Any? {
20 | didSet {
21 | // Update the view, if already loaded.
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/TestHostApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // TestHostApp
4 | //
5 | // Created by Jason Rendel on 11/4/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/TestHostAppOSX/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TestHostAppOSX
4 | //
5 | // Created by Anton Bukarev on 30/11/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // TestHostAppTvOS
4 | //
5 | // Created by Anton Bukarev on 11/12/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperOSX/SwiftKeychainWrapperOSX.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftKeychainWrapperOSX.h
3 | // SwiftKeychainWrapperOSX
4 | //
5 | // Created by Anton Bukarev on 30/11/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftKeychainWrapperOSX.
12 | FOUNDATION_EXPORT double SwiftKeychainWrapperOSXVersionNumber;
13 |
14 | //! Project version string for SwiftKeychainWrapperOSX.
15 | FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperOSXVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTvOS/SwiftKeychainWrapperTvOS.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftKeychainWrapperTvOS.h
3 | // SwiftKeychainWrapperTvOS
4 | //
5 | // Created by Anton Bukarev on 11/12/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftKeychainWrapperTvOS.
12 | FOUNDATION_EXPORT double SwiftKeychainWrapperTvOSVersionNumber;
13 |
14 | //! Project version string for SwiftKeychainWrapperTvOS.
15 | FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperTvOSVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TestHostAppOSXTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 |
--------------------------------------------------------------------------------
/TestHostAppTvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SwiftKeychainWrapper'
3 | s.version = '3.1.0'
4 | s.summary = 'Wrapper for the iOS Keychain written in Swift.'
5 | s.description = <<-DESC
6 | A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to UserDefaults. Supports Access Groups. Written in Swift.'
7 | DESC
8 | s.module_name = "SwiftKeychainWrapper"
9 | s.homepage = 'https://github.com/jrendel/SwiftKeychainWrapper'
10 | s.license = 'MIT'
11 | s.authors = { 'Jason Rendel' => 'jason@jasonrendel.com' }
12 | s.osx.deployment_target = '10.10'
13 | s.tvos.deployment_target = '9.0'
14 | s.ios.deployment_target = '8.0'
15 | s.source = { :git => 'https://github.com/jrendel/SwiftKeychainWrapper.git', :tag => s.version }
16 | s.source_files = 'SwiftKeychainWrapper/*.{h,swift}'
17 | end
18 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0.8
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0.8
23 |
24 |
25 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - App Store.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "2320x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image Wide.imageset",
19 | "role" : "top-shelf-image-wide"
20 | },
21 | {
22 | "size" : "1920x720",
23 | "idiom" : "tv",
24 | "filename" : "Top Shelf Image.imageset",
25 | "role" : "top-shelf-image"
26 | }
27 | ],
28 | "info" : {
29 | "version" : 1,
30 | "author" : "xcode"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 3.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 3.1.0
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperOSX/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSHumanReadableCopyright
22 | Copyright © 2017 Jason Rendel. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIMainStoryboardFile
24 | Main
25 | UIRequiredDeviceCapabilities
26 |
27 | arm64
28 |
29 | UIUserInterfaceStyle
30 | Automatic
31 |
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Jason Rendel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/TestHostAppOSXTests/TestHostAppOSXTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestHostAppOSXTests.swift
3 | // TestHostAppOSXTests
4 | //
5 | // Created by Anton Bukarev on 30/11/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import TestHostAppOSX
11 |
12 | class TestHostAppOSXTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/TestHostAppTvOSTests/TestHostAppTvOSTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestHostAppTvOSTests.swift
3 | // TestHostAppTvOSTests
4 | //
5 | // Created by Anton Bukarev on 11/12/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import TestHostAppTvOS
11 |
12 | class TestHostAppTvOSTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/TestHostAppOSX/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 © 2017 Jason Rendel. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/TestHostAppOSX/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 | }
--------------------------------------------------------------------------------
/TestHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/TestHostApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/TestHostApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/SwiftKeychainWrapper.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftKeychainWrapper.h
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 1/13/15.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 | #import
28 |
29 | //! Project version number for SwiftKeychainWrapper.
30 | FOUNDATION_EXPORT double SwiftKeychainWrapperVersionNumber;
31 |
32 | //! Project version string for SwiftKeychainWrapper.
33 | FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperVersionString[];
34 |
35 | // In this header, you should import all the public headers of your framework using statements like #import
36 |
37 |
38 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/TestObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // testObject.swift
3 | // KeychainWrapper
4 | //
5 | // Created by Jason Rendel on 9/23/14.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 | class TestObject: NSObject, NSCoding {
31 | var objectName = "Name"
32 | var objectRating = 0
33 |
34 | override init() { }
35 |
36 | required init?(coder decoder: NSCoder) {
37 | if let name = decoder.decodeObject(forKey: "objectName") as? String {
38 | self.objectName = name
39 | }
40 |
41 | self.objectRating = decoder.decodeInteger(forKey: "objectRating")
42 | }
43 |
44 | func encode(with encoder: NSCoder) {
45 | encoder.encode(self.objectName, forKey: "objectName")
46 | encoder.encode(self.objectRating, forKey: "objectRating")
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/TestHostApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TestHostApp
4 | //
5 | // Created by Jason Rendel on 11/4/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TestHostAppTvOS
4 | //
5 | // Created by Anton Bukarev on 11/12/2017.
6 | // Copyright © 2017 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperDeleteTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperDeleteTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 3/25/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | #if os(OSX)
12 | import SwiftKeychainWrapperOSX
13 | #elseif os(iOS)
14 | import SwiftKeychainWrapper
15 | #elseif os(tvOS)
16 | import SwiftKeychainWrapperTvOS
17 | #endif
18 |
19 | class KeychainWrapperDeleteTests: XCTestCase {
20 | let testKey = "deleteTestKey"
21 | let testString = "This is a test"
22 |
23 | override func setUp() {
24 | super.setUp()
25 | // Put setup code here. This method is called before the invocation of each test method in the class.
26 | }
27 |
28 | override func tearDown() {
29 | // Put teardown code here. This method is called after the invocation of each test method in the class.
30 | super.tearDown()
31 | }
32 |
33 | func testRemoveAllKeysDeletesSpecificKey() {
34 | // save a value we can test delete on
35 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
36 |
37 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
38 |
39 | // delete all
40 | let removeSuccessful = KeychainWrapper.standard.removeAllKeys()
41 |
42 | XCTAssertTrue(removeSuccessful, "Failed to remove all Keys")
43 |
44 | // confirm our test value was deleted
45 | let retrievedValue = KeychainWrapper.standard.string(forKey: testKey)
46 |
47 | XCTAssertNil(retrievedValue, "Test value was not deleted")
48 | }
49 |
50 | func testWipeKeychainDeletesSpecificKey() {
51 | // save a value we can test delete on
52 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
53 |
54 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
55 |
56 | // delete all
57 | KeychainWrapper.wipeKeychain()
58 |
59 | // confirm our test value was deleted
60 | let retrievedValue = KeychainWrapper.standard.string(forKey: testKey)
61 |
62 | XCTAssertNil(retrievedValue, "Test value was not deleted")
63 |
64 | // clean up keychain
65 | KeychainWrapper.standard.removeObject(forKey: testKey)
66 | }
67 |
68 | func testRemoveAllKeysOnlyRemovesKeysForCurrentServiceName() {
69 |
70 | }
71 |
72 | func testRemoveAllKeysOnlyRemovesKeysForCurrentAccessGroup() {
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 4/25/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | #if os(OSX)
12 | import SwiftKeychainWrapperOSX
13 | #elseif os(iOS)
14 | import SwiftKeychainWrapper
15 | #elseif os(tvOS)
16 | import SwiftKeychainWrapperTvOS
17 | #endif
18 |
19 | class KeychainWrapperTests: XCTestCase {
20 |
21 | override func setUp() {
22 | super.setUp()
23 | // Put setup code here. This method is called before the invocation of each test method in the class.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testCustomInstance() {
32 | let uniqueServiceName = UUID().uuidString
33 | let uniqueAccessGroup = UUID().uuidString
34 | let customKeychainWrapperInstance = KeychainWrapper(serviceName: uniqueServiceName, accessGroup: uniqueAccessGroup)
35 |
36 | XCTAssertNotEqual(customKeychainWrapperInstance.serviceName, KeychainWrapper.standard.serviceName, "Custom instance initialized with unique service name, should not match standard Service Name")
37 | XCTAssertNotEqual(customKeychainWrapperInstance.accessGroup, KeychainWrapper.standard.accessGroup, "Custom instance initialized with unique access group, should not match standard Access Group")
38 | }
39 |
40 | func testAccessibility() {
41 | let accessibilityOptions: [KeychainItemAccessibility] = [
42 | .afterFirstUnlock,
43 | .afterFirstUnlockThisDeviceOnly,
44 | .always,
45 | .whenPasscodeSetThisDeviceOnly,
46 | .alwaysThisDeviceOnly,
47 | .whenUnlocked,
48 | .whenUnlockedThisDeviceOnly
49 | ]
50 |
51 | let key = "testKey"
52 |
53 | for accessibilityOption in accessibilityOptions {
54 | KeychainWrapper.standard.set("Test123", forKey: key, withAccessibility: accessibilityOption)
55 |
56 | let accessibilityForKey = KeychainWrapper.standard.accessibilityOfKey(key)
57 |
58 | let accessibilityDescription = String(describing: accessibilityForKey)
59 |
60 | XCTAssertEqual(accessibilityForKey, accessibilityOption, "Accessibility does not match. Expected: \(accessibilityOption) Found: \(accessibilityDescription)")
61 |
62 | // INFO: If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with
63 | KeychainWrapper.standard.removeObject(forKey: key, withAccessibility: accessibilityOption)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapperTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
57 |
58 |
59 |
60 |
66 |
67 |
69 |
70 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapperOSX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapperTvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/TestHostAppTvOS/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/TestHostApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperPrimitiveValueTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperPrimitiveValueTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 4/1/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | #if os(OSX)
12 | import SwiftKeychainWrapperOSX
13 | #elseif os(iOS)
14 | import SwiftKeychainWrapper
15 | #elseif os(tvOS)
16 | import SwiftKeychainWrapperTvOS
17 | #endif
18 |
19 | class KeychainWrapperPrimitiveValueTests: XCTestCase {
20 | let testKey = "primitiveValueTestKey"
21 | let testInteger: Int = 42
22 | let testBool: Bool = false
23 | let testFloat: Float = 5.25
24 | let testDouble: Double = 10.75
25 |
26 | override func setUp() {
27 | super.setUp()
28 | // Put setup code here. This method is called before the invocation of each test method in the class.
29 | }
30 |
31 | override func tearDown() {
32 | // Put teardown code here. This method is called after the invocation of each test method in the class.
33 | super.tearDown()
34 | }
35 |
36 | func testIntegerSave() {
37 | let valueSaved = KeychainWrapper.standard.set(testInteger, forKey: testKey)
38 |
39 | XCTAssertTrue(valueSaved, "Integer value did not save to Keychain")
40 |
41 | // clean up keychain
42 | KeychainWrapper.standard.removeObject(forKey: testKey)
43 | }
44 |
45 | func testIntegerRetrieval() {
46 | KeychainWrapper.standard.set(testInteger, forKey: testKey)
47 |
48 | if let retrievedValue = KeychainWrapper.standard.integer(forKey: testKey) {
49 | XCTAssertEqual(retrievedValue, testInteger, "Integer value retrieved for key should equal value saved for key")
50 | } else {
51 | XCTFail("Integer value for Key not found")
52 | }
53 | }
54 |
55 | func testBoolSave() {
56 | let valueSaved = KeychainWrapper.standard.set(testBool, forKey: testKey)
57 |
58 | XCTAssertTrue(valueSaved, "Bool value did not save to Keychain")
59 |
60 | // clean up keychain
61 | KeychainWrapper.standard.removeObject(forKey: testKey)
62 | }
63 |
64 | func testBoolRetrieval() {
65 | KeychainWrapper.standard.set(testBool, forKey: testKey)
66 |
67 | if let retrievedValue = KeychainWrapper.standard.bool(forKey: testKey) {
68 | XCTAssertEqual(retrievedValue, testBool, "Bool value retrieved for key should equal value saved for key")
69 | } else {
70 | XCTFail("Bool value for Key not found")
71 | }
72 | }
73 |
74 | func testFloatSave() {
75 | let valueSaved = KeychainWrapper.standard.set(testFloat, forKey: testKey)
76 |
77 | XCTAssertTrue(valueSaved, "Float value did not save to Keychain")
78 |
79 | // clean up keychain
80 | KeychainWrapper.standard.removeObject(forKey: testKey)
81 | }
82 |
83 | func testFloatRetrieval() {
84 | KeychainWrapper.standard.set(testFloat, forKey: testKey)
85 |
86 | if let retrievedValue = KeychainWrapper.standard.float(forKey: testKey) {
87 | XCTAssertEqual(retrievedValue, testFloat, "Float value retrieved for key should equal value saved for key")
88 | } else {
89 | XCTFail("Float value for Key not found")
90 | }
91 | }
92 |
93 | func testDoubleSave() {
94 | let valueSaved = KeychainWrapper.standard.set(testDouble, forKey: testKey)
95 |
96 | XCTAssertTrue(valueSaved, "Double value did not save to Keychain")
97 |
98 | // clean up keychain
99 | KeychainWrapper.standard.removeObject(forKey: testKey)
100 | }
101 |
102 | func testDoubleRetrieval() {
103 | KeychainWrapper.standard.set(testDouble, forKey: testKey)
104 |
105 | if let retrievedValue = KeychainWrapper.standard.double(forKey: testKey) {
106 | XCTAssertEqual(retrievedValue, testDouble, "Double value retrieved for key should equal value saved for key")
107 | } else {
108 | XCTFail("Double value for Key not found")
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/TestHostAppTvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
66 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
85 |
87 |
93 |
94 |
95 |
96 |
98 |
99 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/TestHostAppOSX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
66 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
85 |
87 |
93 |
94 |
95 |
96 |
98 |
99 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
79 |
85 |
86 |
87 |
88 |
89 |
90 |
96 |
97 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/TestHostApp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
80 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/KeychainItemAccessibility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainOptions.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by James Blair on 4/24/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 | protocol KeychainAttrRepresentable {
31 | var keychainAttrValue: CFString { get }
32 | }
33 |
34 | // MARK: - KeychainItemAccessibility
35 | public enum KeychainItemAccessibility {
36 | /**
37 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
38 |
39 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
40 | */
41 | @available(iOS 4, *)
42 | case afterFirstUnlock
43 |
44 | /**
45 | The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
46 |
47 | After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
48 | */
49 | @available(iOS 4, *)
50 | case afterFirstUnlockThisDeviceOnly
51 |
52 | /**
53 | The data in the keychain item can always be accessed regardless of whether the device is locked.
54 |
55 | This is not recommended for application use. Items with this attribute migrate to a new device when using encrypted backups.
56 | */
57 | @available(iOS 4, *)
58 | case always
59 |
60 | /**
61 | The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device.
62 |
63 | This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted.
64 | */
65 | @available(iOS 8, *)
66 | case whenPasscodeSetThisDeviceOnly
67 |
68 | /**
69 | The data in the keychain item can always be accessed regardless of whether the device is locked.
70 |
71 | This is not recommended for application use. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
72 | */
73 | @available(iOS 4, *)
74 | case alwaysThisDeviceOnly
75 |
76 | /**
77 | The data in the keychain item can be accessed only while the device is unlocked by the user.
78 |
79 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups.
80 |
81 | This is the default value for keychain items added without explicitly setting an accessibility constant.
82 | */
83 | @available(iOS 4, *)
84 | case whenUnlocked
85 |
86 | /**
87 | The data in the keychain item can be accessed only while the device is unlocked by the user.
88 |
89 | This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
90 | */
91 | @available(iOS 4, *)
92 | case whenUnlockedThisDeviceOnly
93 |
94 | static func accessibilityForAttributeValue(_ keychainAttrValue: CFString) -> KeychainItemAccessibility? {
95 | for (key, value) in keychainItemAccessibilityLookup {
96 | if value == keychainAttrValue {
97 | return key
98 | }
99 | }
100 |
101 | return nil
102 | }
103 | }
104 |
105 | private let keychainItemAccessibilityLookup: [KeychainItemAccessibility:CFString] = {
106 | var lookup: [KeychainItemAccessibility:CFString] = [
107 | .afterFirstUnlock: kSecAttrAccessibleAfterFirstUnlock,
108 | .afterFirstUnlockThisDeviceOnly: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
109 | .always: kSecAttrAccessibleAlways,
110 | .whenPasscodeSetThisDeviceOnly: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
111 | .alwaysThisDeviceOnly : kSecAttrAccessibleAlwaysThisDeviceOnly,
112 | .whenUnlocked: kSecAttrAccessibleWhenUnlocked,
113 | .whenUnlockedThisDeviceOnly: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
114 | ]
115 |
116 | return lookup
117 | }()
118 |
119 | extension KeychainItemAccessibility : KeychainAttrRepresentable {
120 | internal var keychainAttrValue: CFString {
121 | return keychainItemAccessibilityLookup[self]!
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftKeychainWrapper
2 |
3 | A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift.
4 |
5 | Provides singleton instance that is setup to work for most needs. Use `KeychainWrapper.standard` to access the singleton instance.
6 |
7 | If you need to customize the keychain access to use a custom identifier or access group, you can create your own instance instead of using the singleton instance.
8 |
9 | By default, the Keychain Wrapper saves data as a Generic Password type in the iOS Keychain. It saves items such that they can only be accessed when the app is unlocked and open. If you are not familiar with the iOS Keychain usage, this provides a safe default for using the keychain.
10 |
11 | Users that want to deviate from this default implementation, now can do so in in version 2.0 and up. Each request to save/read a key value now allows you to specify the keychain accessibility for that key.
12 |
13 | ## General Usage
14 |
15 | Add a string value to keychain:
16 | ```
17 | let saveSuccessful: Bool = KeychainWrapper.standard.set("Some String", forKey: "myKey")
18 | ```
19 |
20 | Retrieve a string value from keychain:
21 | ```
22 | let retrievedString: String? = KeychainWrapper.standard.string(forKey: "myKey")
23 | ```
24 |
25 | Remove a string value from keychain:
26 | ```
27 | let removeSuccessful: Bool = KeychainWrapper.standard.removeObject(forKey: "myKey")
28 | ```
29 |
30 | ## Custom Instance
31 |
32 | When the Keychain Wrapper is used, all keys are linked to a common identifier for your app, called the service name. By default this uses your main bundle identifier. However, you may also change it, or store multiple items to the keycahin under different identifiers.
33 |
34 | To share keychain items between your applications, you may specify an access group and use that same access group in each application.
35 |
36 | To set a custom service name identifier or access group, you may now create your own instance of the keychain wrapper as follows:
37 |
38 | ```
39 | let uniqueServiceName = "customServiceName"
40 | let uniqueAccessGroup = "sharedAccessGroupName"
41 | let customKeychainWrapperInstance = KeychainWrapper(serviceName: uniqueServiceName, accessGroup: uniqueAccessGroup)
42 | ```
43 | The custom instance can then be used in place of the shared instance or static accessors:
44 |
45 | ```
46 | let saveSuccessful: Bool = customKeychainWrapperInstance.set("Some String", forKey: "myKey")
47 |
48 | let retrievedString: String? = customKeychainWrapperInstance.string(forKey: "myKey")
49 |
50 | let removeSuccessful: Bool = customKeychainWrapperInstance.removeObject(forKey: "myKey")
51 | ```
52 |
53 | ## Accessibility Options
54 |
55 | By default, all items saved to keychain can only be accessed when the device is unlocked. To change this accessibility, an optional `withAccessibility` param can be set on all requests. The enum `KeychainItemAccessibilty` provides an easy way to select the accessibility level desired:
56 |
57 | ```
58 | KeychainWrapper.standard.set("Some String", forKey: "myKey", withAccessibility: .AfterFirstUnlock)
59 | ```
60 |
61 | ## Installation
62 |
63 | #### CocoaPods
64 | You can use [CocoaPods](http://cocoapods.org/) to install SwiftKeychainWrapper by adding it to your `Podfile`:
65 |
66 | ``` ruby
67 | use_frameworks!
68 | platform :ios, '8.0'
69 |
70 | target 'target_name' do
71 | pod 'SwiftKeychainWrapper'
72 | end
73 | ```
74 |
75 | To use the keychain wrapper in your app, import SwiftKeychainWrapper into the file(s) where you want to use it.
76 |
77 | ```
78 | import SwiftKeychainWrapper
79 | ```
80 |
81 | #### Carthage
82 | You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftKeychainWrapper by adding it to your `Cartfile`.
83 |
84 | Swift 3.0:
85 | ```
86 | github "jrendel/SwiftKeychainWrapper" ~> 3.0
87 | ```
88 |
89 | Swift 2.3:
90 | ```
91 | github "jrendel/SwiftKeychainWrapper" == 2.1.1
92 | ```
93 |
94 | #### Manually
95 | Download and drop ```KeychainWrapper.swift``` and ```KeychainItemAcessibility.swift``` into your project.
96 |
97 |
98 | ## Release History
99 |
100 | * 3.1
101 | * Updates for Swift 3.1
102 |
103 | * 3.0.1
104 | * Added a host app for the unit tests to get around the issue with keychain access not working the same on iOS 10 simulators
105 | * Minor update to readme instructions
106 |
107 | * 3.0
108 | * Swift 3.0 update. Contains breaking API changes. 2.2.0 and 2.2.1 are now rolled into 3.0
109 |
110 | * 2.2.1 (Removed from Cocoapods)
111 | * Syntax updates to be more Swift 3 like
112 |
113 | * 2.2 (Removed from Cocoapods)
114 | * Updated to support Swift 3.0
115 | * Remove deprecated functions (static access)
116 |
117 | * 2.1
118 | * Updated to support Swift 2.3
119 |
120 | * 2.0
121 | * Further changes to more closely align the API with how `NSUserDefaults` works. Access to the default implementation is now done through a singleton instance. Static accessors have been included that wrap this shared instance to maintain backwards compatibility. These will be removed in the next update
122 | * Ability to change keychain service name identifier and access group on the shared instance has been deprecated. Users now have the ability to create their own instance of the keychain if they want to customize these.
123 | * Addtional options have been provided to alter the keychain accessibility for each key value saved.
124 |
125 | * 1.0.11
126 | * Update for Swift 2.0
127 |
128 | * 1.0.10
129 | * Update License info. Merged Pull Request with Carthage support.
130 |
131 | * 1.0.8
132 | * Update for Swift 1.2
133 |
134 | * 1.0.7
135 | * Determined that once provisioned correctly for access groups, using KeychainWrapper on the simulator with access groups works. So I removed the simulator related check and unit tests previously added.
136 |
137 | * 1.0.6
138 | * Support for Access Groups
139 | * SwiftKeychainWrapperExample has been updated to show usage with an Access Group: https://github.com/jrendel/SwiftKeychainWrapperExample
140 |
141 | * Access Groups do not work on the simulator. Apps that are built for the simulator aren't signed, so there's no keychain access group for the simulator to check. This means that all apps can see all keychain items when run on the simulator. Attempting to set an access group will result in a failure when attempting to Add or Update keychain items. Because of this, the Keychain Wrapper detects if it is being using on a simulator and will not set an access group property if one is set. This allows the Keychain Wrapper to still be used on the simulator for development of your app. To properly test Keychain Access Groups, you will need to test on a device.
142 |
143 | * 1.0.5
144 | * This version converts the project to a proper Swift Framework and adds a podspec file to be compatible with the latest CocoaPods pre-release, which now supports Swift.
145 |
146 | * To see an example of usage with CocoaPods, I've created the repo SwiftKeychainWrapperExample: https://github.com/jrendel/SwiftKeychainWrapperExample
147 |
148 | * 1.0.2
149 | * Updated for Xcode 6.1
150 |
151 | ---
152 |
153 | I've been using an Objective-C based wrapper in my own projects for the past couple years. The original library I wrote for myself was based on the following tutorial:
154 |
155 | http://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1
156 |
157 | This is a rewrite of that code in Swift.
158 |
159 | [](https://github.com/Carthage/Carthage)
160 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapperDefaultWrapperTests.swift
3 | // SwiftKeychainWrapper
4 | //
5 | // Created by Jason Rendel on 8/8/16.
6 | // Copyright © 2016 Jason Rendel. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | #if os(OSX)
12 | import SwiftKeychainWrapperOSX
13 | #elseif os(iOS)
14 | import SwiftKeychainWrapper
15 | #elseif os(tvOS)
16 | import SwiftKeychainWrapperTvOS
17 | #endif
18 |
19 | class KeychainWrapperDefaultWrapperTests: XCTestCase {
20 | let testKey = "acessorTestKey"
21 | let testString = "This is a test"
22 |
23 | let testKey2 = "acessorTestKey2"
24 | let testString2 = "Test 2 String"
25 |
26 | override func setUp() {
27 | super.setUp()
28 | // Put setup code here. This method is called before the invocation of each test method in the class.
29 | }
30 |
31 | override func tearDown() {
32 | // Put teardown code here. This method is called after the invocation of each test method in the class.
33 |
34 | // clean up keychain
35 | KeychainWrapper.standard.removeObject(forKey: testKey)
36 | KeychainWrapper.standard.removeObject(forKey: testKey2)
37 |
38 | super.tearDown()
39 | }
40 |
41 | func testDefaultServiceName() {
42 | let bundleIdentifier = Bundle.main.bundleIdentifier
43 | if let bundleIdentifierString = bundleIdentifier {
44 | XCTAssertEqual(KeychainWrapper.standard.serviceName, bundleIdentifierString, "Service Name should be equal to the bundle identifier when it is accessible")
45 | } else {
46 | XCTAssertEqual(KeychainWrapper.standard.serviceName, "SwiftKeychainWrapper", "Service Name should be equal to SwiftKeychainWrapper when the bundle identifier is not accessible")
47 | }
48 | }
49 |
50 | func testDefaultAccessGroup() {
51 | XCTAssertNil(KeychainWrapper.standard.accessGroup, "Access Group should be nil when nothing is set")
52 | }
53 |
54 | func testHasValueForKey() {
55 | XCTAssertFalse(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should not have a value for the test key")
56 |
57 | KeychainWrapper.standard.set(testString, forKey: testKey)
58 |
59 | XCTAssertTrue(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should have a value for the test key after it is set")
60 | }
61 |
62 | func testRemoveObjectFromKeychain() {
63 | KeychainWrapper.standard.set(testString, forKey: testKey)
64 |
65 | XCTAssertTrue(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should have a value for the test key after it is set")
66 |
67 | KeychainWrapper.standard.removeObject(forKey: testKey)
68 |
69 | XCTAssertFalse(KeychainWrapper.standard.hasValue(forKey: testKey), "Keychain should not have a value for the test key after it is removed")
70 | }
71 |
72 | func testStringSave() {
73 | let stringSaved = KeychainWrapper.standard.set(testString, forKey: testKey)
74 |
75 | XCTAssertTrue(stringSaved, "String did not save to Keychain")
76 |
77 | // clean up keychain
78 | KeychainWrapper.standard.removeObject(forKey: testKey)
79 | }
80 |
81 | func testStringRetrieval() {
82 | KeychainWrapper.standard.set(testString, forKey: testKey)
83 |
84 | if let retrievedString = KeychainWrapper.standard.string(forKey: testKey) {
85 | XCTAssertEqual(retrievedString, testString, "String retrieved for key should equal string saved for key")
86 | } else {
87 | XCTFail("String for Key not found")
88 | }
89 | }
90 |
91 | func testStringRetrievalWhenValueDoesNotExist() {
92 | let retrievedString = KeychainWrapper.standard.string(forKey: testKey)
93 | XCTAssertNil(retrievedString, "String for Key should not exist")
94 | }
95 |
96 | func testMultipleStringSave() {
97 | if !KeychainWrapper.standard.set(testString, forKey: testKey) {
98 | XCTFail("String for testKey did not save")
99 | }
100 |
101 | if !KeychainWrapper.standard.set(testString2, forKey: testKey2) {
102 | XCTFail("String for testKey2 did not save")
103 | }
104 |
105 | if let string1Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
106 | XCTAssertEqual(string1Retrieved, testString, "String retrieved for testKey should match string saved to testKey")
107 | } else {
108 | XCTFail("String for testKey could not be retrieved")
109 | }
110 |
111 | if let string2Retrieved = KeychainWrapper.standard.string(forKey: testKey2) {
112 | XCTAssertEqual(string2Retrieved, testString2, "String retrieved for testKey2 should match string saved to testKey2")
113 | } else {
114 | XCTFail("String for testKey2 could not be retrieved")
115 | }
116 | }
117 |
118 | func testMultipleStringsSavedToSameKey() {
119 |
120 | if !KeychainWrapper.standard.set(testString, forKey: testKey) {
121 | XCTFail("String for testKey did not save")
122 | }
123 |
124 | if let string1Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
125 | XCTAssertEqual(string1Retrieved, testString, "String retrieved for testKey after first save should match first string saved testKey")
126 | } else {
127 | XCTFail("String for testKey could not be retrieved")
128 | }
129 |
130 | if !KeychainWrapper.standard.set(testString2, forKey: testKey) {
131 | XCTFail("String for testKey did not update")
132 | }
133 |
134 | if let string2Retrieved = KeychainWrapper.standard.string(forKey: testKey) {
135 | XCTAssertEqual(string2Retrieved, testString2, "String retrieved for testKey after update should match second string saved to testKey")
136 | } else {
137 | XCTFail("String for testKey could not be retrieved after update")
138 | }
139 | }
140 |
141 | func testNSCodingObjectSave() {
142 | let myTestObject = TestObject()
143 | let objectSaved = KeychainWrapper.standard.set(myTestObject, forKey: testKey)
144 |
145 | XCTAssertTrue(objectSaved, "Object that implements NSCoding should save to Keychain")
146 | }
147 |
148 | func testNSCodingObjectRetrieval() {
149 | let testInt: Int = 9
150 | let myTestObject = TestObject()
151 | myTestObject.objectName = testString
152 | myTestObject.objectRating = testInt
153 |
154 | KeychainWrapper.standard.set(myTestObject, forKey: testKey)
155 |
156 | if let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject{
157 | XCTAssertEqual(retrievedObject.objectName, testString, "NSCoding compliant object retrieved for key should have objectName property equal to what it was stored with")
158 | XCTAssertEqual(retrievedObject.objectRating, testInt, "NSCoding compliant object retrieved for key should have objectRating property equal to what it was stored with")
159 | } else {
160 | XCTFail("Object for Key not found")
161 | }
162 | }
163 |
164 | func testNSCodingObjectRetrievalWhenValueDoesNotExist() {
165 | let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject
166 | XCTAssertNil(retrievedObject, "Object for Key should not exist")
167 | }
168 |
169 | func testDataSave() {
170 | let testData = testString.data(using: String.Encoding.utf8)
171 |
172 | if let data = testData {
173 | let dataSaved = KeychainWrapper.standard.set(data, forKey: testKey)
174 |
175 | XCTAssertTrue(dataSaved, "Data did not save to Keychain")
176 | } else {
177 | XCTFail("Failed to create Data")
178 | }
179 | }
180 |
181 | func testDataRetrieval() {
182 | guard let testData = testString.data(using: String.Encoding.utf8) else {
183 | XCTFail("Failed to create Data")
184 | return
185 | }
186 |
187 | KeychainWrapper.standard.set(testData, forKey: testKey)
188 |
189 | guard let retrievedData = KeychainWrapper.standard.data(forKey: testKey) else {
190 | XCTFail("Data for Key not found")
191 | return
192 | }
193 |
194 | if KeychainWrapper.standard.dataRef(forKey: testKey) == nil {
195 | XCTFail("Data references for Key not found")
196 | }
197 |
198 | if let retrievedString = String(data: retrievedData, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) {
199 | XCTAssertEqual(retrievedString, testString, "String retrieved from data for key should equal string saved as data for key")
200 | } else {
201 | XCTFail("Output Data for key does not match input. ")
202 | }
203 | }
204 |
205 | func testDataRetrievalWhenValueDoesNotExist() {
206 | let retrievedData = KeychainWrapper.standard.data(forKey: testKey)
207 | XCTAssertNil(retrievedData, "Data for Key should not exist")
208 |
209 | let retrievedDataRef = KeychainWrapper.standard.dataRef(forKey: testKey)
210 | XCTAssertNil(retrievedDataRef, "Data ref for Key should not exist")
211 | }
212 |
213 | func testKeysEmpty() {
214 | let keys = KeychainWrapper.standard.allKeys()
215 | XCTAssertEqual(keys, [], "Empty keychain should not contain keys")
216 | }
217 |
218 | func testKeysOneKey() {
219 | let result = KeychainWrapper.standard.set("testString", forKey: testKey)
220 |
221 | let keys = KeychainWrapper.standard.allKeys()
222 | print(keys)
223 | XCTAssertEqual(keys, [testKey], "Keychain should contain the inserted key")
224 | }
225 |
226 | func testKeysMultipleKeys() {
227 | KeychainWrapper.standard.set(testString, forKey: testKey)
228 | KeychainWrapper.standard.set(testString2, forKey: testKey2)
229 |
230 | let keys = KeychainWrapper.standard.allKeys()
231 | XCTAssertEqual(keys, [testKey, testKey2], "Keychain should contain the inserted keys")
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/SwiftKeychainWrapper/KeychainWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainWrapper.swift
3 | // KeychainWrapper
4 | //
5 | // Created by Jason Rendel on 9/23/14.
6 | // Copyright (c) 2014 Jason Rendel. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import Foundation
29 |
30 |
31 | private let SecMatchLimit: String! = kSecMatchLimit as String
32 | private let SecReturnData: String! = kSecReturnData as String
33 | private let SecReturnPersistentRef: String! = kSecReturnPersistentRef as String
34 | private let SecValueData: String! = kSecValueData as String
35 | private let SecAttrAccessible: String! = kSecAttrAccessible as String
36 | private let SecClass: String! = kSecClass as String
37 | private let SecAttrService: String! = kSecAttrService as String
38 | private let SecAttrGeneric: String! = kSecAttrGeneric as String
39 | private let SecAttrAccount: String! = kSecAttrAccount as String
40 | private let SecAttrAccessGroup: String! = kSecAttrAccessGroup as String
41 | private let SecReturnAttributes: String = kSecReturnAttributes as String
42 |
43 | /// KeychainWrapper is a class to help make Keychain access in Swift more straightforward. It is designed to make accessing the Keychain services more like using NSUserDefaults, which is much more familiar to people.
44 | open class KeychainWrapper {
45 |
46 | @available(*, deprecated: 2.2.1, message: "KeychainWrapper.defaultKeychainWrapper is deprecated, use KeychainWrapper.standard instead")
47 | public static let defaultKeychainWrapper = KeychainWrapper.standard
48 |
49 | /// Default keychain wrapper access
50 | public static let standard = KeychainWrapper()
51 |
52 | /// ServiceName is used for the kSecAttrService property to uniquely identify this keychain accessor. If no service name is specified, KeychainWrapper will default to using the bundleIdentifier.
53 | private (set) public var serviceName: String
54 |
55 | /// AccessGroup is used for the kSecAttrAccessGroup property to identify which Keychain Access Group this entry belongs to. This allows you to use the KeychainWrapper with shared keychain access between different applications.
56 | private (set) public var accessGroup: String?
57 |
58 | private static let defaultServiceName: String = {
59 | return Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper"
60 | }()
61 |
62 | private convenience init() {
63 | self.init(serviceName: KeychainWrapper.defaultServiceName)
64 | }
65 |
66 | /// Create a custom instance of KeychainWrapper with a custom Service Name and optional custom access group.
67 | ///
68 | /// - parameter serviceName: The ServiceName for this instance. Used to uniquely identify all keys stored using this keychain wrapper instance.
69 | /// - parameter accessGroup: Optional unique AccessGroup for this instance. Use a matching AccessGroup between applications to allow shared keychain access.
70 | public init(serviceName: String, accessGroup: String? = nil) {
71 | self.serviceName = serviceName
72 | self.accessGroup = accessGroup
73 | }
74 |
75 | // MARK:- Public Methods
76 |
77 | /// Checks if keychain data exists for a specified key.
78 | ///
79 | /// - parameter forKey: The key to check for.
80 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
81 | /// - returns: True if a value exists for the key. False otherwise.
82 | open func hasValue(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
83 | if let _ = data(forKey: key, withAccessibility: accessibility) {
84 | return true
85 | } else {
86 | return false
87 | }
88 | }
89 |
90 | open func accessibilityOfKey(_ key: String) -> KeychainItemAccessibility? {
91 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key)
92 |
93 | // Remove accessibility attribute
94 | keychainQueryDictionary.removeValue(forKey: SecAttrAccessible)
95 |
96 | // Limit search results to one
97 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
98 |
99 | // Specify we want SecAttrAccessible returned
100 | keychainQueryDictionary[SecReturnAttributes] = kCFBooleanTrue
101 |
102 | // Search
103 | var result: AnyObject?
104 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
105 |
106 | guard status == noErr, let resultsDictionary = result as? [String:AnyObject], let accessibilityAttrValue = resultsDictionary[SecAttrAccessible] as? String else {
107 | return nil
108 | }
109 |
110 | return KeychainItemAccessibility.accessibilityForAttributeValue(accessibilityAttrValue as CFString)
111 | }
112 |
113 | /// Get the keys of all keychain entries matching the current ServiceName and AccessGroup if one is set.
114 | open func allKeys() -> Set {
115 | var keychainQueryDictionary: [String:Any] = [
116 | SecClass: kSecClassGenericPassword,
117 | SecAttrService: serviceName,
118 | SecReturnAttributes: kCFBooleanTrue,
119 | SecMatchLimit: kSecMatchLimitAll,
120 | ]
121 |
122 | if let accessGroup = self.accessGroup {
123 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
124 | }
125 |
126 | var result: AnyObject?
127 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
128 |
129 | guard status == errSecSuccess else { return [] }
130 |
131 | var keys = Set()
132 | if let results = result as? [[AnyHashable: Any]] {
133 | for attributes in results {
134 | if let accountName = attributes[SecAttrAccount] as? String {
135 | keys.insert(accountName)
136 | }
137 | }
138 | }
139 | return keys
140 | }
141 |
142 | // MARK: Public Getters
143 |
144 | open func integer(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Int? {
145 | guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
146 | return nil
147 | }
148 |
149 | return numberValue.intValue
150 | }
151 |
152 | open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Float? {
153 | guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
154 | return nil
155 | }
156 |
157 | return numberValue.floatValue
158 | }
159 |
160 | open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Double? {
161 | guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
162 | return nil
163 | }
164 |
165 | return numberValue.doubleValue
166 | }
167 |
168 | open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool? {
169 | guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
170 | return nil
171 | }
172 |
173 | return numberValue.boolValue
174 | }
175 |
176 | /// Returns a string value for a specified key.
177 | ///
178 | /// - parameter forKey: The key to lookup data for.
179 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
180 | /// - returns: The String associated with the key if it exists. If no data exists, or the data found cannot be encoded as a string, returns nil.
181 | open func string(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> String? {
182 | guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
183 | return nil
184 | }
185 |
186 | return String(data: keychainData, encoding: String.Encoding.utf8) as String?
187 | }
188 |
189 | /// Returns an object that conforms to NSCoding for a specified key.
190 | ///
191 | /// - parameter forKey: The key to lookup data for.
192 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
193 | /// - returns: The decoded object associated with the key if it exists. If no data exists, or the data found cannot be decoded, returns nil.
194 | open func object(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> NSCoding? {
195 | guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
196 | return nil
197 | }
198 |
199 | return NSKeyedUnarchiver.unarchiveObject(with: keychainData) as? NSCoding
200 | }
201 |
202 |
203 | /// Returns a Data object for a specified key.
204 | ///
205 | /// - parameter forKey: The key to lookup data for.
206 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
207 | /// - returns: The Data object associated with the key if it exists. If no data exists, returns nil.
208 | open func data(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
209 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
210 |
211 | // Limit search results to one
212 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
213 |
214 | // Specify we want Data/CFData returned
215 | keychainQueryDictionary[SecReturnData] = kCFBooleanTrue
216 |
217 | // Search
218 | var result: AnyObject?
219 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
220 |
221 | return status == noErr ? result as? Data : nil
222 | }
223 |
224 |
225 | /// Returns a persistent data reference object for a specified key.
226 | ///
227 | /// - parameter forKey: The key to lookup data for.
228 | /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
229 | /// - returns: The persistent data reference object associated with the key if it exists. If no data exists, returns nil.
230 | open func dataRef(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
231 | var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
232 |
233 | // Limit search results to one
234 | keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
235 |
236 | // Specify we want persistent Data/CFData reference returned
237 | keychainQueryDictionary[SecReturnPersistentRef] = kCFBooleanTrue
238 |
239 | // Search
240 | var result: AnyObject?
241 | let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
242 |
243 | return status == noErr ? result as? Data : nil
244 | }
245 |
246 | // MARK: Public Setters
247 |
248 | @discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
249 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
250 | }
251 |
252 | @discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
253 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
254 | }
255 |
256 | @discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
257 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
258 | }
259 |
260 | @discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
261 | return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
262 | }
263 |
264 | /// Save a String value to the keychain associated with a specified key. If a String value already exists for the given key, the string will be overwritten with the new value.
265 | ///
266 | /// - parameter value: The String value to save.
267 | /// - parameter forKey: The key to save the String under.
268 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
269 | /// - returns: True if the save was successful, false otherwise.
270 | @discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
271 | if let data = value.data(using: .utf8) {
272 | return set(data, forKey: key, withAccessibility: accessibility)
273 | } else {
274 | return false
275 | }
276 | }
277 |
278 | /// Save an NSCoding compliant object to the keychain associated with a specified key. If an object already exists for the given key, the object will be overwritten with the new value.
279 | ///
280 | /// - parameter value: The NSCoding compliant object to save.
281 | /// - parameter forKey: The key to save the object under.
282 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
283 | /// - returns: True if the save was successful, false otherwise.
284 | @discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
285 | let data = NSKeyedArchiver.archivedData(withRootObject: value)
286 |
287 | return set(data, forKey: key, withAccessibility: accessibility)
288 | }
289 |
290 | /// Save a Data object to the keychain associated with a specified key. If data already exists for the given key, the data will be overwritten with the new value.
291 | ///
292 | /// - parameter value: The Data object to save.
293 | /// - parameter forKey: The key to save the object under.
294 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
295 | /// - returns: True if the save was successful, false otherwise.
296 | @discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
297 | var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
298 |
299 | keychainQueryDictionary[SecValueData] = value
300 |
301 | if let accessibility = accessibility {
302 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
303 | } else {
304 | // Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
305 | keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
306 | }
307 |
308 | let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
309 |
310 | if status == errSecSuccess {
311 | return true
312 | } else if status == errSecDuplicateItem {
313 | return update(value, forKey: key, withAccessibility: accessibility)
314 | } else {
315 | return false
316 | }
317 | }
318 |
319 | @available(*, deprecated: 2.2.1, message: "remove is deprecated, use removeObject instead")
320 | @discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
321 | return removeObject(forKey: key, withAccessibility: accessibility)
322 | }
323 |
324 | /// Remove an object associated with a specified key. If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with.
325 | ///
326 | /// - parameter forKey: The key value to remove data for.
327 | /// - parameter withAccessibility: Optional accessibility level to use when looking up the keychain item.
328 | /// - returns: True if successful, false otherwise.
329 | @discardableResult open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
330 | let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
331 |
332 | // Delete
333 | let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
334 |
335 | if status == errSecSuccess {
336 | return true
337 | } else {
338 | return false
339 | }
340 | }
341 |
342 | /// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set.
343 | open func removeAllKeys() -> Bool {
344 | // Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
345 | var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
346 |
347 | // Uniquely identify this keychain accessor
348 | keychainQueryDictionary[SecAttrService] = serviceName
349 |
350 | // Set the keychain access group if defined
351 | if let accessGroup = self.accessGroup {
352 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
353 | }
354 |
355 | let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
356 |
357 | if status == errSecSuccess {
358 | return true
359 | } else {
360 | return false
361 | }
362 | }
363 |
364 | /// Remove all keychain data, including data not added through keychain wrapper.
365 | ///
366 | /// - Warning: This may remove custom keychain entries you did not add via SwiftKeychainWrapper.
367 | ///
368 | open class func wipeKeychain() {
369 | deleteKeychainSecClass(kSecClassGenericPassword) // Generic password items
370 | deleteKeychainSecClass(kSecClassInternetPassword) // Internet password items
371 | deleteKeychainSecClass(kSecClassCertificate) // Certificate items
372 | deleteKeychainSecClass(kSecClassKey) // Cryptographic key items
373 | deleteKeychainSecClass(kSecClassIdentity) // Identity items
374 | }
375 |
376 | // MARK:- Private Methods
377 |
378 | /// Remove all items for a given Keychain Item Class
379 | ///
380 | ///
381 | @discardableResult private class func deleteKeychainSecClass(_ secClass: AnyObject) -> Bool {
382 | let query = [SecClass: secClass]
383 | let status: OSStatus = SecItemDelete(query as CFDictionary)
384 |
385 | if status == errSecSuccess {
386 | return true
387 | } else {
388 | return false
389 | }
390 | }
391 |
392 | /// Update existing data associated with a specified key name. The existing data will be overwritten by the new data.
393 | private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
394 | var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
395 | let updateDictionary = [SecValueData:value]
396 |
397 | // on update, only set accessibility if passed in
398 | if let accessibility = accessibility {
399 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
400 | }
401 |
402 | // Update
403 | let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
404 |
405 | if status == errSecSuccess {
406 | return true
407 | } else {
408 | return false
409 | }
410 | }
411 |
412 | /// Setup the keychain query dictionary used to access the keychain on iOS for a specified key name. Takes into account the Service Name and Access Group if one is set.
413 | ///
414 | /// - parameter forKey: The key this query is for
415 | /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item. If none is provided, will default to .WhenUnlocked
416 | /// - returns: A dictionary with all the needed properties setup to access the keychain on iOS
417 | private func setupKeychainQueryDictionary(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> [String:Any] {
418 | // Setup default access as generic password (rather than a certificate, internet password, etc)
419 | var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
420 |
421 | // Uniquely identify this keychain accessor
422 | keychainQueryDictionary[SecAttrService] = serviceName
423 |
424 | // Only set accessibiilty if its passed in, we don't want to default it here in case the user didn't want it set
425 | if let accessibility = accessibility {
426 | keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
427 | }
428 |
429 | // Set the keychain access group if defined
430 | if let accessGroup = self.accessGroup {
431 | keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
432 | }
433 |
434 | // Uniquely identify the account who will be accessing the keychain
435 | let encodedIdentifier: Data? = key.data(using: String.Encoding.utf8)
436 |
437 | keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
438 |
439 | keychainQueryDictionary[SecAttrAccount] = key
440 |
441 | return keychainQueryDictionary
442 | }
443 | }
444 |
--------------------------------------------------------------------------------
/TestHostAppOSX/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
--------------------------------------------------------------------------------