Skip to content

Commit 8bf5667

Browse files
committed
split out KeychainWrapper
1 parent 8d984cf commit 8bf5667

File tree

2 files changed

+123
-3
lines changed

2 files changed

+123
-3
lines changed

swift-sdk.xcodeproj/project.pbxproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
5531CDAC22A997A4000D05E2 /* IterableInboxViewControllerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585DF9022A877E6000A32B9 /* IterableInboxViewControllerUITests.swift */; };
1616
5531CDAE22A9C992000D05E2 /* ClassExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531CDAD22A9C992000D05E2 /* ClassExtensionsTests.swift */; };
1717
5536781F2576FF9000DB3652 /* IterableUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5536781E2576FF9000DB3652 /* IterableUtilTests.swift */; };
18+
5555425028BED1B400DB5D20 /* KeychainWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5555424F28BED1B400DB5D20 /* KeychainWrapper.swift */; };
1819
556FB1EA244FAF6A00EDF6BD /* InAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556FB1E9244FAF6A00EDF6BD /* InAppPresenter.swift */; };
1920
557AE6BF24A56E5E00B57750 /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557AE6BE24A56E5E00B57750 /* Auth.swift */; };
2021
5585DF8F22A73390000A32B9 /* IterableInboxViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */; };
@@ -396,6 +397,7 @@
396397
552A0AA6280E1FDA00A80963 /* DeepLinkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkManager.swift; sourceTree = "<group>"; };
397398
5531CDAD22A9C992000D05E2 /* ClassExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassExtensionsTests.swift; sourceTree = "<group>"; };
398399
5536781E2576FF9000DB3652 /* IterableUtilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableUtilTests.swift; sourceTree = "<group>"; };
400+
5555424F28BED1B400DB5D20 /* KeychainWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainWrapper.swift; sourceTree = "<group>"; };
399401
556FB1E9244FAF6A00EDF6BD /* InAppPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPresenter.swift; sourceTree = "<group>"; };
400402
557AE6BE24A56E5E00B57750 /* Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = "<group>"; };
401403
5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxViewControllerTests.swift; sourceTree = "<group>"; };
@@ -1263,10 +1265,11 @@
12631265
ACE34AB121376ACB00691224 /* Local Storage */ = {
12641266
isa = PBXGroup;
12651267
children = (
1266-
ACE34AB62139D70B00691224 /* LocalStorageProtocol.swift */,
1267-
ACE34AB221376B1000691224 /* LocalStorage.swift */,
1268-
AC52C5B52729CE44000DCDCF /* IterableUserDefaults.swift */,
12691268
AC52C5B9272A8BC2000DCDCF /* IterableKeychain.swift */,
1269+
AC52C5B52729CE44000DCDCF /* IterableUserDefaults.swift */,
1270+
5555424F28BED1B400DB5D20 /* KeychainWrapper.swift */,
1271+
ACE34AB221376B1000691224 /* LocalStorage.swift */,
1272+
ACE34AB62139D70B00691224 /* LocalStorageProtocol.swift */,
12701273
);
12711274
name = "Local Storage";
12721275
sourceTree = "<group>";
@@ -1823,6 +1826,7 @@
18231826
AC84510922910A0C0052BB8F /* RequestCreator.swift in Sources */,
18241827
ACB8273F22372A5C00DB17D3 /* IterableHtmlMessageViewController.swift in Sources */,
18251828
AC5812F624F3A90F007E6D36 /* OfflineRequestProcessor.swift in Sources */,
1829+
5555425028BED1B400DB5D20 /* KeychainWrapper.swift in Sources */,
18261830
AC81918A22713A400014955E /* AbstractDiffCalculator.swift in Sources */,
18271831
ACA95D2D275494A100AF4666 /* InboxViewRepresentable.swift in Sources */,
18281832
AC684A88222F4FDD00F29749 /* InAppDisplayer.swift in Sources */,
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//
2+
// Copyright © 2022 Iterable. All rights reserved.
3+
//
4+
5+
import Foundation
6+
7+
/// Basic wrapper for keychain
8+
/// This should have no dependency on Iterable classes
9+
class KeychainWrapper {
10+
init(serviceName: String = Const.Keychain.serviceName) {
11+
self.serviceName = serviceName
12+
}
13+
14+
@discardableResult
15+
func set(_ value: Data, forKey key: String) -> Bool {
16+
var keychainQueryDictionary: [String: Any] = setupKeychainQueryDictionary(forKey: key)
17+
18+
keychainQueryDictionary[SecValueData] = value
19+
20+
// Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
21+
keychainQueryDictionary[SecAttrAccessible] = SecAttrAccessibleWhenUnlocked
22+
23+
let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
24+
25+
if status == errSecSuccess {
26+
return true
27+
} else if status == errSecDuplicateItem {
28+
return update(value, forKey: key)
29+
} else {
30+
return false
31+
}
32+
}
33+
34+
func data(forKey key: String) -> Data? {
35+
var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key)
36+
37+
// Limit search results to one
38+
keychainQueryDictionary[SecMatchLimit] = SecMatchLimitOne
39+
40+
// Specify we want Data/CFData returned
41+
keychainQueryDictionary[SecReturnData] = CFBooleanTrue
42+
43+
// Search
44+
var result: AnyObject?
45+
let status = SecItemCopyMatching(keychainQueryDictionary as CFDictionary, &result)
46+
47+
return status == noErr ? result as? Data : nil
48+
}
49+
50+
@discardableResult
51+
func removeValue(forKey key: String) -> Bool {
52+
let keychainQueryDictionary: [String: Any] = setupKeychainQueryDictionary(forKey: key)
53+
54+
// Delete
55+
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
56+
57+
return status == errSecSuccess
58+
}
59+
60+
@discardableResult
61+
func removeAll() -> Bool {
62+
var keychainQueryDictionary: [String: Any] = [SecClass: SecClassGenericPassword]
63+
64+
keychainQueryDictionary[SecAttrService] = serviceName
65+
66+
let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
67+
68+
return status == errSecSuccess
69+
}
70+
71+
private let serviceName: String
72+
73+
private func setupKeychainQueryDictionary(forKey key: String) -> [String: Any] {
74+
// Setup default access as generic password (rather than a certificate, internet password, etc)
75+
var keychainQueryDictionary: [String: Any] = [SecClass: SecClassGenericPassword]
76+
77+
// Uniquely identify this keychain accessor
78+
keychainQueryDictionary[SecAttrService] = serviceName
79+
80+
// Uniquely identify the account who will be accessing the keychain
81+
let encodedIdentifier: Data? = key.data(using: .utf8)
82+
83+
keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
84+
85+
keychainQueryDictionary[SecAttrAccount] = encodedIdentifier
86+
87+
keychainQueryDictionary[SecAttrSynchronizable] = CFBooleanFalse
88+
89+
return keychainQueryDictionary
90+
}
91+
92+
private func update(_ value: Data, forKey key: String) -> Bool {
93+
let keychainQueryDictionary: [String: Any] = setupKeychainQueryDictionary(forKey: key)
94+
let updateDictionary = [SecValueData: value]
95+
96+
// Update
97+
let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
98+
99+
return status == errSecSuccess
100+
}
101+
102+
private let SecValueData = kSecValueData as String
103+
private let SecAttrAccessible: String = kSecAttrAccessible as String
104+
private let SecAttrAccessibleWhenUnlocked = kSecAttrAccessibleWhenUnlocked
105+
private let SecClass: String = kSecClass as String
106+
private let SecClassGenericPassword = kSecClassGenericPassword
107+
private let SecAttrService: String = kSecAttrService as String
108+
private let SecAttrGeneric: String = kSecAttrGeneric as String
109+
private let SecAttrAccount: String = kSecAttrAccount as String
110+
private let SecAttrSynchronizable: String = kSecAttrSynchronizable as String
111+
private let CFBooleanTrue = kCFBooleanTrue
112+
private let CFBooleanFalse = kCFBooleanFalse
113+
private let SecMatchLimit: String = kSecMatchLimit as String
114+
private let SecMatchLimitOne = kSecMatchLimitOne
115+
private let SecReturnData: String = kSecReturnData as String
116+
}

0 commit comments

Comments
 (0)