diff --git a/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme b/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
new file mode 100644
index 0000000..0ed6014
--- /dev/null
+++ b/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Demo/Demo/UIKitContentViewController.swift b/Demo/Demo/UIKitContentViewController.swift
index 06eb910..1e370b8 100644
--- a/Demo/Demo/UIKitContentViewController.swift
+++ b/Demo/Demo/UIKitContentViewController.swift
@@ -362,4 +362,3 @@ extension UIKitContentViewController: PayPalMessageViewEventDelegate {
statusTextView.text = "Applied"
}
}
-// swiftlint:disable:this file_length
diff --git a/Sources/PayPalMessages/Extensions/UserDefaults.swift b/Sources/PayPalMessages/Extensions/UserDefaults.swift
index be66444..09cf687 100644
--- a/Sources/PayPalMessages/Extensions/UserDefaults.swift
+++ b/Sources/PayPalMessages/Extensions/UserDefaults.swift
@@ -7,12 +7,11 @@ extension UserDefaults {
}
// holds the data for a merchant profile, of type PayPalMessageMerchantData
- static var merchantProfileData: Data? {
- get {
- standard.data(forKey: Key.merchantProfileData.rawValue)
- }
- set {
- standard.set(newValue, forKey: Key.merchantProfileData.rawValue)
- }
+ static func getMerchantProfileData(forClientID clientID: String, merchantID: String?) -> Data? {
+ return standard.data(forKey: "\(Key.merchantProfileData.rawValue).\(clientID).\(merchantID ?? "default")")
+ }
+
+ static func setMerchantProfileData(_ value: Data?, forClientID clientID: String, merchantID: String?) {
+ standard.set(value, forKey: "\(Key.merchantProfileData.rawValue).\(clientID).\(merchantID ?? "default")")
}
}
diff --git a/Sources/PayPalMessages/IO/MerchantProfileProvider.swift b/Sources/PayPalMessages/IO/MerchantProfileProvider.swift
index 1c93a84..c969681 100644
--- a/Sources/PayPalMessages/IO/MerchantProfileProvider.swift
+++ b/Sources/PayPalMessages/IO/MerchantProfileProvider.swift
@@ -4,6 +4,7 @@ protocol MerchantProfileHashGetable {
func getMerchantProfileHash(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (String?) -> Void
)
}
@@ -25,14 +26,15 @@ class MerchantProfileProvider: MerchantProfileHashGetable {
func getMerchantProfileHash(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (String?) -> Void
) {
let currentDate = Date()
// hash must be inside ttl and non-null
- guard let merchantProfileData = getCachedMerchantProfileData(),
+ guard let merchantProfileData = getCachedMerchantProfileData(clientID: clientID, merchantID: merchantID),
currentDate < merchantProfileData.ttlHard else {
- requestMerchantProfile(environment: environment, clientID: clientID) { merchantProfiledData in
+ requestMerchantProfile(environment: environment, clientID: clientID, merchantID: merchantID) { merchantProfiledData in
guard let merchantProfiledData = merchantProfiledData else {
onCompletion(nil)
return
@@ -46,7 +48,7 @@ class MerchantProfileProvider: MerchantProfileHashGetable {
// if date is outside soft-ttl window, re-request data
if currentDate > merchantProfileData.ttlSoft {
// ignores the response as it will return hashed value
- requestMerchantProfile(environment: environment, clientID: clientID) { _ in }
+ requestMerchantProfile(environment: environment, clientID: clientID, merchantID: merchantID) { _ in }
}
onCompletion(merchantProfileData.disabled ? nil : merchantProfileData.hash)
@@ -57,17 +59,22 @@ class MerchantProfileProvider: MerchantProfileHashGetable {
private func requestMerchantProfile(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (MerchantProfileData?) -> Void
) {
- merchantProfileRequest.fetchMerchantProfile(environment: environment, clientID: clientID) { [weak self] result in
+ merchantProfileRequest.fetchMerchantProfile(
+ environment: environment,
+ clientID: clientID,
+ merchantID: merchantID
+ ) { [weak self] result in
switch result {
case .success(let merchantProfileData):
- log(.info, "Merchant Request Hash succeeded with \(merchantProfileData.hash)")
- self?.setCachedMerchantProfileData(merchantProfileData)
+ log(.debug, "Merchant Request Hash succeeded with \(merchantProfileData.hash)")
+ self?.setCachedMerchantProfileData(merchantProfileData, clientID: clientID, merchantID: merchantID)
onCompletion(merchantProfileData)
case .failure(let error):
- log(.info, "Merchant Request Hash failed with \(error.localizedDescription)")
+ log(.debug, "Merchant Request Hash failed with \(error.localizedDescription)")
onCompletion(nil)
}
}
@@ -75,16 +82,16 @@ class MerchantProfileProvider: MerchantProfileHashGetable {
// MARK: - User Defaults Methods
- private func getCachedMerchantProfileData() -> MerchantProfileData? {
- guard let cachedData = UserDefaults.merchantProfileData else {
+ private func getCachedMerchantProfileData(clientID: String, merchantID: String?) -> MerchantProfileData? {
+ guard let cachedData = UserDefaults.getMerchantProfileData(forClientID: clientID, merchantID: merchantID) else {
return nil
}
return try? JSONDecoder().decode(MerchantProfileData.self, from: cachedData)
}
- private func setCachedMerchantProfileData(_ data: MerchantProfileData) {
+ private func setCachedMerchantProfileData(_ data: MerchantProfileData, clientID: String, merchantID: String?) {
let encodedData = try? JSONEncoder().encode(data)
- UserDefaults.merchantProfileData = encodedData
+ UserDefaults.setMerchantProfileData(encodedData, forClientID: clientID, merchantID: merchantID)
}
}
diff --git a/Sources/PayPalMessages/IO/MerchantProfileRequest.swift b/Sources/PayPalMessages/IO/MerchantProfileRequest.swift
index 7a04476..b135c6f 100644
--- a/Sources/PayPalMessages/IO/MerchantProfileRequest.swift
+++ b/Sources/PayPalMessages/IO/MerchantProfileRequest.swift
@@ -4,6 +4,7 @@ protocol MerchantProfileRequestable {
func fetchMerchantProfile(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (Result) -> Void
)
}
@@ -27,14 +28,15 @@ class MerchantProfileRequest: MerchantProfileRequestable {
func fetchMerchantProfile(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (Result) -> Void
) {
- guard let url = environment.url(.merchantProfile, ["client_id": clientID]) else {
+ guard let url = environment.url(.merchantProfile, ["client_id": clientID, "merchant_id": merchantID]) else {
onCompletion(.failure(RequestError.invalidClientID))
return
}
- log(.info, "fetcheMerchantProfile URL is \(url)")
+ log(.debug, "fetcheMerchantProfile URL is \(url)")
fetch(url, headers: headers, session: environment.urlSession) { data, _, error in
guard let data = data, error == nil else {
diff --git a/Sources/PayPalMessages/PayPalMessageViewModel.swift b/Sources/PayPalMessages/PayPalMessageViewModel.swift
index fe2accf..a8b10dc 100644
--- a/Sources/PayPalMessages/PayPalMessageViewModel.swift
+++ b/Sources/PayPalMessages/PayPalMessageViewModel.swift
@@ -221,7 +221,11 @@ class PayPalMessageViewModel: PayPalMessageModalEventDelegate {
stateDelegate.onLoading(messageView)
}
- merchantProfileProvider.getMerchantProfileHash(environment: environment, clientID: clientID) { [weak self] profileHash in
+ merchantProfileProvider.getMerchantProfileHash(
+ environment: environment,
+ clientID: clientID,
+ merchantID: merchantID
+ ) { [weak self] profileHash in
guard let self else { return }
let parameters = self.makeRequestParameters(merchantProfileHash: profileHash)
diff --git a/Tests/PayPalMessagesTests/MerchantProfileProviderTests.swift b/Tests/PayPalMessagesTests/MerchantProfileProviderTests.swift
index a48f002..e30a61a 100644
--- a/Tests/PayPalMessagesTests/MerchantProfileProviderTests.swift
+++ b/Tests/PayPalMessagesTests/MerchantProfileProviderTests.swift
@@ -4,12 +4,19 @@ import XCTest
final class MerchantProfileProviderTests: XCTestCase {
+ let clientID = "testclientid"
+ let clientID2 = "testclientid2"
+ let merchantID = "testmerchantid"
+ let merchantID2 = "testmerchantid2"
+
override func setUp() {
super.setUp()
- UserDefaults.standard.removeObject(
- forKey: UserDefaults.Key.merchantProfileData.rawValue
- )
+ // Clear out merchant profile cache
+ let dictionary = UserDefaults.standard.dictionaryRepresentation()
+ for key in dictionary.keys {
+ UserDefaults.standard.removeObject(forKey: key)
+ }
}
// MARK: - Tests
@@ -23,7 +30,7 @@ final class MerchantProfileProviderTests: XCTestCase {
XCTAssertEqual(requestMock.requestsPerformed, 0)
// retrieve hash and check requests performed
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
XCTAssertNotNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
}
@@ -35,32 +42,64 @@ final class MerchantProfileProviderTests: XCTestCase {
let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
// retrieve hash and check requests performed
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
let firstHash = hash
XCTAssertNotNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
// after retrieving it again, the hash should not be empty -- and no new request performed
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { secondHash in
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { secondHash in
XCTAssertEqual(firstHash, secondHash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
}
}
}
+ func testCacheMissWhenDifferentMerchant() {
+ let requestMock = MerchantProfileRequestMock(scenario: .success)
+ let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
+
+
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: nil) { hash in
+ let firstHash = hash
+ XCTAssertNotNil(hash)
+ XCTAssertEqual(requestMock.requestsPerformed, 1)
+
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID2, merchantID: nil) { secondHash in
+ XCTAssertNotEqual(firstHash, secondHash)
+ XCTAssertEqual(requestMock.requestsPerformed, 2)
+
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { hash in
+ let firstHash = hash
+ XCTAssertNotNil(hash)
+ XCTAssertEqual(requestMock.requestsPerformed, 3)
+
+ provider.getMerchantProfileHash(
+ environment: .live,
+ clientID: self.clientID,
+ merchantID: self.merchantID2
+ ) { secondHash in
+ XCTAssertNotEqual(firstHash, secondHash)
+ XCTAssertEqual(requestMock.requestsPerformed, 4)
+ }
+ }
+ }
+ }
+ }
+
// simulates a request with soft ttl expired, returns value but performs additional request
func testTtlSoftExpired() {
let requestMock = MerchantProfileRequestMock(scenario: .ttlSoftExpired)
let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
// perform first request
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
let firstHash = hash
XCTAssertNotNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
// perform another request, cached value will be returned but another request perfoms on background
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { secondHash in
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { secondHash in
XCTAssertEqual(firstHash, secondHash)
XCTAssertEqual(requestMock.requestsPerformed, 2)
}
@@ -73,13 +112,13 @@ final class MerchantProfileProviderTests: XCTestCase {
let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
// perform first request
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
let firstHash = hash
XCTAssertNotNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
// perform another request, new request performed
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { secondHash in
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { secondHash in
XCTAssertNotEqual(firstHash, secondHash)
XCTAssertEqual(requestMock.requestsPerformed, 2)
}
@@ -93,11 +132,11 @@ final class MerchantProfileProviderTests: XCTestCase {
let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
// perform first request -- null expected
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
XCTAssertNil(hash)
// perform another request, null expected and used cached result
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { hash in
XCTAssertNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 1)
}
@@ -110,11 +149,11 @@ final class MerchantProfileProviderTests: XCTestCase {
let provider = MerchantProfileProvider(merchantProfileRequest: requestMock)
// perform first request -- null expected
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: clientID, merchantID: merchantID) { hash in
XCTAssertNil(hash)
// perform another request, null expected and new request performed
- provider.getMerchantProfileHash(environment: .live, clientID: "testclientid") { hash in
+ provider.getMerchantProfileHash(environment: .live, clientID: self.clientID, merchantID: self.merchantID) { hash in
XCTAssertNil(hash)
XCTAssertEqual(requestMock.requestsPerformed, 2)
}
diff --git a/Tests/PayPalMessagesTests/Mocks/MerchantProfileRequestMock.swift b/Tests/PayPalMessagesTests/Mocks/MerchantProfileRequestMock.swift
index f9eb736..66c38c2 100644
--- a/Tests/PayPalMessagesTests/Mocks/MerchantProfileRequestMock.swift
+++ b/Tests/PayPalMessagesTests/Mocks/MerchantProfileRequestMock.swift
@@ -34,6 +34,7 @@ class MerchantProfileRequestMock: MerchantProfileRequestable {
func fetchMerchantProfile(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (Result) -> Void
) {
requestsPerformed += 1
diff --git a/Tests/PayPalMessagesTests/Mocks/PayPalMessageMerchantProviderMock.swift b/Tests/PayPalMessagesTests/Mocks/PayPalMessageMerchantProviderMock.swift
index 96f077e..98e37b2 100644
--- a/Tests/PayPalMessagesTests/Mocks/PayPalMessageMerchantProviderMock.swift
+++ b/Tests/PayPalMessagesTests/Mocks/PayPalMessageMerchantProviderMock.swift
@@ -17,6 +17,7 @@ class MerchantProfileProviderMock: MerchantProfileHashGetable {
func getMerchantProfileHash(
environment: Environment,
clientID: String,
+ merchantID: String?,
onCompletion: @escaping (String?) -> Void
) {
switch scenario {
diff --git a/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift b/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift
index 5cc5fba..66ff383 100644
--- a/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift
+++ b/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift
@@ -372,7 +372,6 @@ final class PayPalMessageViewModelTests: XCTestCase {
private func assert(_ mockRequest: PayPalMessageRequestMock, calledTimes count: Int) {
let predicate = NSPredicate { _, _ in
- print(mockRequest.requestsPerformed, count)
return mockRequest.requestsPerformed == count
}
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: mockRequest)