From 1f7503ae6dbaae48bfb2495c0bf6c13ede919447 Mon Sep 17 00:00:00 2001 From: Maurice van Breukelen Date: Fri, 7 May 2021 12:37:14 +0200 Subject: [PATCH 1/2] Set deployment target from 9.0 to 11.0 and fix deprecation warnings --- SwiftKeychainWrapper.podspec | 2 +- .../project.pbxproj | 6 +++--- .../xcschemes/SwiftKeychainWrapper.xcscheme | 2 +- .../KeychainItemAccessibility.swift | 2 -- SwiftKeychainWrapper/KeychainWrapper.swift | 20 ++++++++++--------- .../KeychainWrapperDefaultWrapperTests.swift | 4 ++-- .../KeychainWrapperTests.swift | 2 -- SwiftKeychainWrapperTests/TestObject.swift | 5 ++++- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/SwiftKeychainWrapper.podspec b/SwiftKeychainWrapper.podspec index 6fa14e1..a20bc22 100644 --- a/SwiftKeychainWrapper.podspec +++ b/SwiftKeychainWrapper.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.homepage = 'https://github.com/jrendel/SwiftKeychainWrapper' s.license = 'MIT' s.authors = { 'Jason Rendel' => 'jason@jasonrendel.com' } - s.ios.deployment_target = '9.0' + s.ios.deployment_target = '11.0' s.tvos.deployment_target = '11.0' s.swift_version = ['4.2', '5.0'] s.source = { :git => 'https://github.com/jrendel/SwiftKeychainWrapper.git', :tag => s.version } diff --git a/SwiftKeychainWrapper.xcodeproj/project.pbxproj b/SwiftKeychainWrapper.xcodeproj/project.pbxproj index a2b8e94..202a03a 100644 --- a/SwiftKeychainWrapper.xcodeproj/project.pbxproj +++ b/SwiftKeychainWrapper.xcodeproj/project.pbxproj @@ -247,7 +247,7 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0810; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = "Jason Rendel"; TargetAttributes = { 47E429C51DCCE2FD002BE498 = { @@ -488,7 +488,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; ONLY_ACTIVE_ARCH = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -526,7 +526,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; }; diff --git a/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme b/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme index e963d4f..f8d0c79 100644 --- a/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme +++ b/SwiftKeychainWrapper.xcodeproj/xcshareddata/xcschemes/SwiftKeychainWrapper.xcscheme @@ -1,6 +1,6 @@ Int? { - guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else { + guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { return nil } @@ -156,7 +156,7 @@ open class KeychainWrapper { } open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Float? { - guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else { + guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { return nil } @@ -164,7 +164,7 @@ open class KeychainWrapper { } open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Double? { - guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else { + guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { return nil } @@ -172,7 +172,7 @@ open class KeychainWrapper { } open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool? { - guard let numberValue = object(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) as? NSNumber else { + guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { return nil } @@ -199,12 +199,12 @@ open class KeychainWrapper { /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item. /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false /// - returns: The decoded object associated with the key if it exists. If no data exists, or the data found cannot be decoded, returns nil. - open func object(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> NSCoding? { + open func object(forKey key: String, ofClass cls: DecodedObjectType.Type, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> DecodedObjectType? where DecodedObjectType : NSObject, DecodedObjectType : NSCoding { guard let keychainData = data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { return nil } - return NSKeyedUnarchiver.unarchiveObject(with: keychainData) as? NSCoding + return try? NSKeyedUnarchiver.unarchivedObject(ofClass: cls, from: keychainData) } @@ -293,9 +293,11 @@ open class KeychainWrapper { /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item. /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false /// - returns: True if the save was successful, false otherwise. - @discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - let data = NSKeyedArchiver.archivedData(withRootObject: value) - + @discardableResult open func set(_ value: T, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool where T : NSSecureCoding { + guard let data = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true) else { + return false + } + return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } diff --git a/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift b/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift index 2a6fb7d..98ae1e5 100644 --- a/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift +++ b/SwiftKeychainWrapperTests/KeychainWrapperDefaultWrapperTests.swift @@ -146,7 +146,7 @@ class KeychainWrapperDefaultWrapperTests: XCTestCase { KeychainWrapper.standard.set(myTestObject, forKey: testKey) - if let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject{ + if let retrievedObject = KeychainWrapper.standard.object(forKey: testKey, ofClass: TestObject.self) { XCTAssertEqual(retrievedObject.objectName, testString, "NSCoding compliant object retrieved for key should have objectName property equal to what it was stored with") XCTAssertEqual(retrievedObject.objectRating, testInt, "NSCoding compliant object retrieved for key should have objectRating property equal to what it was stored with") } else { @@ -155,7 +155,7 @@ class KeychainWrapperDefaultWrapperTests: XCTestCase { } func testNSCodingObjectRetrievalWhenValueDoesNotExist() { - let retrievedObject = KeychainWrapper.standard.object(forKey: testKey) as? TestObject + let retrievedObject = KeychainWrapper.standard.object(forKey: testKey, ofClass: TestObject.self) XCTAssertNil(retrievedObject, "Object for Key should not exist") } diff --git a/SwiftKeychainWrapperTests/KeychainWrapperTests.swift b/SwiftKeychainWrapperTests/KeychainWrapperTests.swift index c790c12..b13e299 100644 --- a/SwiftKeychainWrapperTests/KeychainWrapperTests.swift +++ b/SwiftKeychainWrapperTests/KeychainWrapperTests.swift @@ -34,9 +34,7 @@ class KeychainWrapperTests: XCTestCase { let accessibilityOptions: [KeychainItemAccessibility] = [ .afterFirstUnlock, .afterFirstUnlockThisDeviceOnly, - .always, .whenPasscodeSetThisDeviceOnly, - .alwaysThisDeviceOnly, .whenUnlocked, .whenUnlockedThisDeviceOnly ] diff --git a/SwiftKeychainWrapperTests/TestObject.swift b/SwiftKeychainWrapperTests/TestObject.swift index 5d6a508..974c531 100644 --- a/SwiftKeychainWrapperTests/TestObject.swift +++ b/SwiftKeychainWrapperTests/TestObject.swift @@ -27,12 +27,15 @@ import Foundation -class TestObject: NSObject, NSCoding { +class TestObject: NSObject, NSSecureCoding { + var objectName = "Name" var objectRating = 0 override init() { } + static var supportsSecureCoding: Bool { true } + required init?(coder decoder: NSCoder) { if let name = decoder.decodeObject(forKey: "objectName") as? String { self.objectName = name From c2e61925f4050e6ed7dc6d35d3bdcd8268f254b0 Mon Sep 17 00:00:00 2001 From: Maurice van Breukelen Date: Fri, 7 May 2021 12:55:31 +0200 Subject: [PATCH 2/2] Update to Swift 5 --- .../KeychainItemAccessibility.swift | 16 +--- SwiftKeychainWrapper/KeychainWrapper.swift | 93 +++++-------------- 2 files changed, 28 insertions(+), 81 deletions(-) diff --git a/SwiftKeychainWrapper/KeychainItemAccessibility.swift b/SwiftKeychainWrapper/KeychainItemAccessibility.swift index 4f1385c..7dbd874 100644 --- a/SwiftKeychainWrapper/KeychainItemAccessibility.swift +++ b/SwiftKeychainWrapper/KeychainItemAccessibility.swift @@ -92,30 +92,22 @@ public enum KeychainItemAccessibility { case whenUnlockedThisDeviceOnly static func accessibilityForAttributeValue(_ keychainAttrValue: CFString) -> KeychainItemAccessibility? { - for (key, value) in keychainItemAccessibilityLookup { - if value == keychainAttrValue { - return key - } - } - - return nil + keychainItemAccessibilityLookup.first { $0.value == keychainAttrValue }?.key } } private let keychainItemAccessibilityLookup: [KeychainItemAccessibility:CFString] = { - var lookup: [KeychainItemAccessibility:CFString] = [ + return [ .afterFirstUnlock: kSecAttrAccessibleAfterFirstUnlock, .afterFirstUnlockThisDeviceOnly: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, .whenPasscodeSetThisDeviceOnly: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .whenUnlocked: kSecAttrAccessibleWhenUnlocked, .whenUnlockedThisDeviceOnly: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] - - return lookup }() extension KeychainItemAccessibility : KeychainAttrRepresentable { - internal var keychainAttrValue: CFString { - return keychainItemAccessibilityLookup[self]! + var keychainAttrValue: CFString { + keychainItemAccessibilityLookup[self]! } } diff --git a/SwiftKeychainWrapper/KeychainWrapper.swift b/SwiftKeychainWrapper/KeychainWrapper.swift index 3ebb6ca..5b4f0fc 100644 --- a/SwiftKeychainWrapper/KeychainWrapper.swift +++ b/SwiftKeychainWrapper/KeychainWrapper.swift @@ -56,9 +56,7 @@ open class KeychainWrapper { /// 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. private (set) public var accessGroup: String? - private static let defaultServiceName: String = { - return Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper" - }() + private static let defaultServiceName = Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper" private convenience init() { self.init(serviceName: KeychainWrapper.defaultServiceName) @@ -82,11 +80,7 @@ open class KeychainWrapper { /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false /// - returns: True if a value exists for the key. False otherwise. open func hasValue(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - if let _ = data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) { - return true - } else { - return false - } + data(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) != nil } open func accessibilityOfKey(_ key: String) -> KeychainItemAccessibility? { @@ -109,7 +103,7 @@ open class KeychainWrapper { return nil } - return KeychainItemAccessibility.accessibilityForAttributeValue(accessibilityAttrValue as CFString) + return .accessibilityForAttributeValue(accessibilityAttrValue as CFString) } /// Get the keys of all keychain entries matching the current ServiceName and AccessGroup if one is set. @@ -148,35 +142,19 @@ open class KeychainWrapper { // MARK: Public Getters open func integer(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Int? { - guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { - return nil - } - - return numberValue.intValue + object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable)?.intValue } open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Float? { - guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { - return nil - } - - return numberValue.floatValue + object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable)?.floatValue } open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Double? { - guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { - return nil - } - - return numberValue.doubleValue + object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable)?.doubleValue } open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool? { - guard let numberValue = object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable) else { - return nil - } - - return numberValue.boolValue + object(forKey: key, ofClass: NSNumber.self, withAccessibility: accessibility, isSynchronizable: isSynchronizable)?.boolValue } /// Returns a string value for a specified key. @@ -190,7 +168,7 @@ open class KeychainWrapper { return nil } - return String(data: keychainData, encoding: String.Encoding.utf8) as String? + return String(data: keychainData, encoding: .utf8) } /// Returns an object that conforms to NSCoding for a specified key. @@ -256,19 +234,19 @@ open class KeychainWrapper { // MARK: Public Setters @discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) + set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } @discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) + set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } @discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) + set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } @discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) + set(NSNumber(value: value), forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } /// 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. @@ -279,11 +257,8 @@ open class KeychainWrapper { /// - parameter isSynchronizable: A bool that describes if the item should be synchronizable, to be synched with the iCloud. If none is provided, will default to false /// - returns: True if the save was successful, false otherwise. @discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - if let data = value.data(using: .utf8) { - return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) - } else { - return false - } + guard let data = value.data(using: .utf8) else { return false } + return set(data, forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } /// 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. @@ -320,7 +295,7 @@ open class KeychainWrapper { keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue } - let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil) + let status = SecItemAdd(keychainQueryDictionary as CFDictionary, nil) if status == errSecSuccess { return true @@ -333,7 +308,7 @@ open class KeychainWrapper { @available(*, deprecated, message: "remove is deprecated since version 2.2.1, use removeObject instead") @discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil, isSynchronizable: Bool = false) -> Bool { - return removeObject(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) + removeObject(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) } /// 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. @@ -346,13 +321,8 @@ open class KeychainWrapper { let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility, isSynchronizable: isSynchronizable) // Delete - let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary) - - if status == errSecSuccess { - return true - } else { - return false - } + let status = SecItemDelete(keychainQueryDictionary as CFDictionary) + return status == errSecSuccess } /// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set. @@ -368,13 +338,8 @@ open class KeychainWrapper { keychainQueryDictionary[SecAttrAccessGroup] = accessGroup } - let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary) - - if status == errSecSuccess { - return true - } else { - return false - } + let status = SecItemDelete(keychainQueryDictionary as CFDictionary) + return status == errSecSuccess } /// Remove all keychain data, including data not added through keychain wrapper. @@ -396,13 +361,8 @@ open class KeychainWrapper { /// @discardableResult private class func deleteKeychainSecClass(_ secClass: AnyObject) -> Bool { let query = [SecClass: secClass] - let status: OSStatus = SecItemDelete(query as CFDictionary) - - if status == errSecSuccess { - return true - } else { - return false - } + let status = SecItemDelete(query as CFDictionary) + return status == errSecSuccess } /// Update existing data associated with a specified key name. The existing data will be overwritten by the new data. @@ -416,13 +376,8 @@ open class KeychainWrapper { } // Update - let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary) - - if status == errSecSuccess { - return true - } else { - return false - } + let status = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary) + return status == errSecSuccess } /// 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.