Skip to content

Commit 4a8a047

Browse files
committed
resolve accidental merge conflict
1 parent 75a336b commit 4a8a047

File tree

7 files changed

+158
-10
lines changed

7 files changed

+158
-10
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 */,

swift-sdk/Constants.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum Const {
6363
static let email = "itbl_email"
6464
static let userId = "itbl_userid"
6565
static let authToken = "itbl_auth_token"
66+
static let lastPushPayload = "itbl_last_push_payload"
6667
}
6768
}
6869

swift-sdk/Internal/InternalIterableAPI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
4141
}
4242

4343
var lastPushPayload: [AnyHashable: Any]? {
44-
localStorage.getPayload(currentDate: dateProvider.currentDate)
44+
localStorage.getLastPushPayload(dateProvider.currentDate)
4545
}
4646

4747
var attributionInfo: IterableAttributionInfo? {

swift-sdk/Internal/IterableUserDefaults.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ class IterableUserDefaults {
8282
try? save(dict: payload, withKey: .payload, andExpiration: expiration)
8383
}
8484

85+
func getLastPushPayloadAndExpirationPair() -> (payload: [AnyHashable: Any]?, expiration: Date?)? {
86+
guard let encodedEnvelope = userDefaults.value(forKey: UserDefaultsKey.payload.value) as? Data else {
87+
return nil
88+
}
89+
90+
do {
91+
let envelope = try JSONDecoder().decode(Envelope.self, from: encodedEnvelope)
92+
let decoded = try JSONSerialization.jsonObject(with: envelope.payload, options: []) as? [AnyHashable: Any]
93+
94+
return (payload: decoded, envelope.expiration)
95+
} catch {
96+
return nil
97+
}
98+
}
99+
85100
// MARK: Private implementation
86101

87102
private let userDefaults: UserDefaults
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+
}

swift-sdk/Internal/LocalStorage.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,22 @@ struct LocalStorage: LocalStorageProtocol {
7575
iterableUserDefaults.save(attributionInfo: attributionInfo, withExpiration: expiration)
7676
}
7777

78-
func getPayload(currentDate: Date) -> [AnyHashable: Any]? {
79-
iterableUserDefaults.getPayload(currentDate: currentDate)
78+
func getLastPushPayload(_ currentDate: Date) -> [AnyHashable: Any]? {
79+
return keychain.getLastPushPayload(currentDate: currentDate)
8080
}
8181

82-
func save(payload: [AnyHashable: Any]?, withExpiration expiration: Date?) {
83-
iterableUserDefaults.save(payload: payload, withExpiration: expiration)
82+
func saveLastPushPayload(_ payload: [AnyHashable: Any]?, withExpiration expiration: Date?) {
83+
keychain.setLastPushPayload(payload, withExpiration: expiration)
8484
}
8585

8686
func upgrade() {
8787
ITBInfo()
8888

8989
/// moves `email`, `userId`, and `authToken` from `UserDefaults` to `IterableKeychain`
9090
moveAuthDataFromUserDefaultsToKeychain()
91+
92+
/// moves `lastPushPayload` from `UserDefaults` to `IterableKeychain`
93+
moveLastPushPayloadFromUserDefaultsToKeychain()
9194
}
9295

9396
// MARK: Private
@@ -117,4 +120,13 @@ struct LocalStorage: LocalStorageProtocol {
117120
ITBInfo("UPDATED: moved userId from UserDefaults to IterableKeychain")
118121
}
119122
}
123+
124+
private func moveLastPushPayloadFromUserDefaultsToKeychain() {
125+
// using current date rather than `DateProvider` for convenience
126+
if let (userDefaultLastPushPayload, expiration) = iterableUserDefaults.getLastPushPayloadAndExpirationPair() {
127+
keychain.setLastPushPayload(userDefaultLastPushPayload, withExpiration: expiration)
128+
129+
ITBInfo("UPDATED: moved lastPushPayload from UserDefaults to IterableKeychain")
130+
}
131+
}
120132
}

swift-sdk/Internal/LocalStorageProtocol.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ protocol LocalStorageProtocol {
1414
var offlineMode: Bool { get set }
1515
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo?
1616
func save(attributionInfo: IterableAttributionInfo?, withExpiration expiration: Date?)
17-
func getPayload(currentDate: Date) -> [AnyHashable: Any]?
18-
func save(payload: [AnyHashable: Any]?, withExpiration: Date?)
17+
func getLastPushPayload(_ currentDate: Date) -> [AnyHashable: Any]?
18+
func saveLastPushPayload(_ payload: [AnyHashable: Any]?, withExpiration expiration: Date?)
1919
func upgrade()
2020
}
2121

0 commit comments

Comments
 (0)