Skip to content

Commit aa1bbeb

Browse files
authored
Merge pull request #3902 from Crebs/@W-19387499
@W-19387499 fix bug where notification weren't getting encrypted properly
2 parents 0bee96a + d104235 commit aa1bbeb

6 files changed

Lines changed: 174 additions & 63 deletions

File tree

libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
23EDDF022DE0F9EF0024AD39 /* URLRequest+RestRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EDDF012DE0F9EF0024AD39 /* URLRequest+RestRequestTests.swift */; };
5656
23EED88A2E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */; };
5757
23EED8912E2ACF3900646B10 /* MockNavigationAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EED8902E2ACF3100646B10 /* MockNavigationAction.swift */; };
58+
23F200AC2E551C890091C5F5 /* ActionTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23F200AB2E551C890091C5F5 /* ActionTypeTests.swift */; };
5859
444B95D01E83251900908C61 /* UIColor+SFColorsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 444B95CF1E83251900908C61 /* UIColor+SFColorsTests.m */; };
5960
4F06AF731C49A16A00F70798 /* NSURL+SFStringUtilsTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF5D1C49A16A00F70798 /* NSURL+SFStringUtilsTests.h */; };
6061
4F06AF751C49A16A00F70798 /* SalesforceOAuthUnitTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF5F1C49A16A00F70798 /* SalesforceOAuthUnitTests.h */; };
@@ -573,6 +574,7 @@
573574
23EDDF012DE0F9EF0024AD39 /* URLRequest+RestRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "URLRequest+RestRequestTests.swift"; path = "SalesforceSDKCoreTests/URLRequest+RestRequestTests.swift"; sourceTree = SOURCE_ROOT; };
574575
23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SFOAuthCoordinatorTests.swift; path = SalesforceSDKCoreTests/SFOAuthCoordinatorTests.swift; sourceTree = SOURCE_ROOT; };
575576
23EED8902E2ACF3100646B10 /* MockNavigationAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNavigationAction.swift; sourceTree = "<group>"; };
577+
23F200AB2E551C890091C5F5 /* ActionTypeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ActionTypeTests.swift; path = SalesforceSDKCoreTests/ActionTypeTests.swift; sourceTree = SOURCE_ROOT; };
576578
444B95CF1E83251900908C61 /* UIColor+SFColorsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+SFColorsTests.m"; path = "SalesforceSDKCoreTests/UIColor+SFColorsTests.m"; sourceTree = SOURCE_ROOT; };
577579
4F06AF5D1C49A16A00F70798 /* NSURL+SFStringUtilsTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURL+SFStringUtilsTests.h"; path = "SalesforceSDKCoreTests/NSURL+SFStringUtilsTests.h"; sourceTree = SOURCE_ROOT; };
578580
4F06AF5E1C49A16A00F70798 /* NSURL+SFStringUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+SFStringUtilsTests.m"; path = "SalesforceSDKCoreTests/NSURL+SFStringUtilsTests.m"; sourceTree = SOURCE_ROOT; };
@@ -1038,6 +1040,7 @@
10381040
4F7EB3F61BFFC84700768720 /* SalesforceSDKCoreTests */ = {
10391041
isa = PBXGroup;
10401042
children = (
1043+
23F200AB2E551C890091C5F5 /* ActionTypeTests.swift */,
10411044
23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */,
10421045
23D96B752E145B400004B06A /* DomainDiscoveryCoordinatorTests.swift */,
10431046
237C18722E450B710008015C /* DecryptStreamTests.swift */,
@@ -2233,6 +2236,7 @@
22332236
69DFE06C2B969C25000906E4 /* PushNotificationDecryptionTests.swift in Sources */,
22342237
4F7EB4161BFFC8D700768720 /* SDKCommonNSDataTests.m in Sources */,
22352238
CE81A9C81E9C26F900F3D0AD /* SFUserAccountManagerNotificationsTests.m in Sources */,
2239+
23F200AC2E551C890091C5F5 /* ActionTypeTests.swift in Sources */,
22362240
4F7EB4171BFFC8D700768720 /* SFEncryptionKeyTests.m in Sources */,
22372241
69848CBD2364063E00893E57 /* SFSDKPushNotificationDataProvider.m in Sources */,
22382242
23A4C7492D0CAFCF00DF55EB /* NativeLoginManagerTests.swift in Sources */,

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Extensions/PushNotificationManager+ActionableNotifications.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ internal extension PushNotificationManager {
136136
}
137137
}
138138

139-
func storeNotification(types: [NotificationType], with account: UserAccount) {
139+
func storeNotification(types: [NotificationType], with account: UserAccount) throws {
140140
account.notificationTypes = types
141+
try UserAccountManager.shared.upsert(account)
141142
}
142143

143144
func setNotificationCategories(types: [NotificationType]) {

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/NotificationType.swift

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ public class NotificationTypesResponse: NSObject, Codable {
1212

1313
@objc(SFSDKNotificationType)
1414
@objcMembers
15-
public class NotificationType: NSObject, Codable {
15+
public class NotificationType: NSObject, Codable, NSSecureCoding {
16+
public static var supportsSecureCoding: Bool { true }
1617
public let type: String
1718
public let apiName: String
1819
public let label: String
@@ -25,6 +26,24 @@ public class NotificationType: NSObject, Codable {
2526
self.actionGroups = actionGroups
2627
}
2728

29+
// Required by NSSecureCoding (Objective-C protocol)
30+
public required convenience init?(coder aDecoder: NSCoder) {
31+
guard let type = aDecoder.decodeObject(of: NSString.self, forKey: "type") as String?,
32+
let apiName = aDecoder.decodeObject(of: NSString.self, forKey: "apiName") as String?,
33+
let label = aDecoder.decodeObject(of: NSString.self, forKey: "label") as String? else {
34+
return nil
35+
}
36+
let actionGroups = aDecoder.decodeObject(of: [NSArray.self, ActionGroup.self], forKey: "actionGroups") as? [ActionGroup]
37+
self.init(type: type, apiName: apiName, label: label, actionGroups: actionGroups)
38+
}
39+
40+
public func encode(with aCoder: NSCoder) {
41+
aCoder.encode(type, forKey: "type")
42+
aCoder.encode(apiName, forKey: "apiName")
43+
aCoder.encode(label, forKey: "label")
44+
aCoder.encode(actionGroups, forKey: "actionGroups")
45+
}
46+
2847
/** Creates a new NotificationType with only the specified actions
2948
* - Parameter allowedActionTypes: Set of action types to keep
3049
* - Returns: A new NotificationType with filtered actions
@@ -54,19 +73,35 @@ public class NotificationType: NSObject, Codable {
5473

5574
@objc(SFSDKActionGroup)
5675
@objcMembers
57-
public class ActionGroup: NSObject, Codable {
76+
public class ActionGroup: NSObject, Codable, NSSecureCoding {
5877
public let name: String
5978
public let actions: [Action]
60-
79+
public static var supportsSecureCoding: Bool { true }
80+
6181
public init(name: String, actions: [Action]) {
6282
self.name = name
6383
self.actions = actions
6484
}
85+
86+
// Required by NSSecureCoding (Objective-C protocol)
87+
public required convenience init?(coder aDecoder: NSCoder) {
88+
guard let name = aDecoder.decodeObject(of: NSString.self, forKey: "name") as String?,
89+
let actions = aDecoder.decodeObject(of: [NSArray.self, Action.self], forKey: "actions") as? [Action] else {
90+
return nil
91+
}
92+
self.init(name: name, actions: actions)
93+
}
94+
95+
public func encode(with aCoder: NSCoder) {
96+
aCoder.encode(name, forKey: "name")
97+
aCoder.encode(actions, forKey: "actions")
98+
}
6599
}
66100

67101
@objc(SFSDKAction)
68102
@objcMembers
69-
public class Action: NSObject, Codable {
103+
public class Action: NSObject, Codable, NSSecureCoding {
104+
public static var supportsSecureCoding: Bool { true }
70105
public let name: String
71106
public let identifier: String
72107
public let label: String
@@ -83,6 +118,24 @@ public class Action: NSObject, Codable {
83118
self.label = label
84119
self.type = type
85120
}
121+
122+
// Required by NSSecureCoding (Objective-C protocol)
123+
public required convenience init?(coder aDecoder: NSCoder) {
124+
guard let name = aDecoder.decodeObject(of: NSString.self, forKey: "name") as String?,
125+
let identifier = aDecoder.decodeObject(of: NSString.self, forKey: "identifier") as String?,
126+
let label = aDecoder.decodeObject(of: NSString.self, forKey: "label") as String?,
127+
let type = aDecoder.decodeObject(of: NSString.self, forKey: "type") as String? else {
128+
return nil
129+
}
130+
self.init(name: name, identifier: identifier, label: label, type: type)
131+
}
132+
133+
public func encode(with aCoder: NSCoder) {
134+
aCoder.encode(name, forKey: "name")
135+
aCoder.encode(identifier, forKey: "identifier")
136+
aCoder.encode(label, forKey: "label")
137+
aCoder.encode(type, forKey: "type")
138+
}
86139
}
87140

88141
@objc(SFSDKActionResultRepresentation)

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/PushNotificationManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ public class PushNotificationManager: NSObject {
364364

365365
do {
366366
let types = try await fetchNotificationTypesFromAPI(with: restClient)
367-
storeNotification(types: types, with: account)
367+
try storeNotification(types: types, with: account)
368368
setNotificationCategories(types: types)
369369
} catch {
370370
SFSDKCoreLogger.d(PushNotificationManager.self, message: "API fetch failed: \(error.localizedDescription). Trying cache...")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import XCTest
2+
@testable import SalesforceSDKCore
3+
4+
class ActionTypeTests: XCTestCase {
5+
func testActionTypeDecoding() throws {
6+
// Given
7+
let json = """
8+
{
9+
"name": "test",
10+
"actionKey": "test_key",
11+
"label": "Test Label",
12+
"type": "NotificationApiAction"
13+
}
14+
"""
15+
let jsonData = json.data(using: .utf8)!
16+
// When
17+
let action = try JSONDecoder().decode(Action.self, from: jsonData)
18+
// Then
19+
XCTAssertEqual(action.type, "NotificationApiAction")
20+
}
21+
22+
func testActionTypeDecodingForeground() throws {
23+
// Given
24+
let json = """
25+
{
26+
"name": "test",
27+
"actionKey": "test_key",
28+
"label": "Test Label",
29+
"type": "foreground"
30+
}
31+
"""
32+
let jsonData = json.data(using: .utf8)!
33+
// When
34+
let action = try JSONDecoder().decode(Action.self, from: jsonData)
35+
// Then
36+
XCTAssertEqual(action.type, "foreground")
37+
}
38+
39+
func testActionTypeDecodingInvalidType() throws {
40+
// Given
41+
let json = """
42+
{
43+
"name": "test",
44+
"actionKey": "test_key",
45+
"label": "Test Label",
46+
"invalidType": "invalidType"
47+
}
48+
"""
49+
let jsonData = json.data(using: .utf8)!
50+
// When, Then
51+
XCTAssertThrowsError(try JSONDecoder().decode(Action.self, from: jsonData))
52+
}
53+
}
54+
55+
extension ActionTypeTests {
56+
func testAction_NSSecureCoding_RoundTrip() throws {
57+
// Given
58+
let original = Action(name: "test", identifier: "test_key", label: "Test Label", type: "NotificationApiAction")
59+
// When
60+
let data = try NSKeyedArchiver.archivedData(withRootObject: original, requiringSecureCoding: true)
61+
let unarchived = try NSKeyedUnarchiver.unarchivedObject(ofClass: Action.self, from: data)
62+
// Then
63+
XCTAssertNotNil(unarchived)
64+
XCTAssertEqual(unarchived?.name, original.name)
65+
XCTAssertEqual(unarchived?.identifier, original.identifier)
66+
XCTAssertEqual(unarchived?.label, original.label)
67+
XCTAssertEqual(unarchived?.type, original.type)
68+
}
69+
70+
func testActionGroup_NSSecureCoding_RoundTrip() throws {
71+
// Given
72+
let action = Action(name: "test", identifier: "test_key", label: "Test Label", type: "NotificationApiAction")
73+
let original = ActionGroup(name: "group1", actions: [action])
74+
// When
75+
let data = try NSKeyedArchiver.archivedData(withRootObject: original, requiringSecureCoding: true)
76+
let unarchived = try NSKeyedUnarchiver.unarchivedObject(ofClass: ActionGroup.self, from: data)
77+
// Then
78+
XCTAssertNotNil(unarchived)
79+
XCTAssertEqual(unarchived?.name, original.name)
80+
XCTAssertEqual(unarchived?.actions.count, 1)
81+
XCTAssertEqual(unarchived?.actions.first?.name, action.name)
82+
}
83+
84+
func testNotificationType_NSSecureCoding_RoundTrip() throws {
85+
// Given
86+
let action = Action(name: "test", identifier: "test_key", label: "Test Label", type: "NotificationApiAction")
87+
let group = ActionGroup(name: "group1", actions: [action])
88+
let original = NotificationType(type: "type1", apiName: "api1", label: "Label1", actionGroups: [group])
89+
// When
90+
let data = try NSKeyedArchiver.archivedData(withRootObject: original, requiringSecureCoding: true)
91+
let unarchived = try NSKeyedUnarchiver.unarchivedObject(ofClass: NotificationType.self, from: data)
92+
// Then
93+
XCTAssertNotNil(unarchived)
94+
XCTAssertEqual(unarchived?.type, original.type)
95+
XCTAssertEqual(unarchived?.apiName, original.apiName)
96+
XCTAssertEqual(unarchived?.label, original.label)
97+
XCTAssertEqual(unarchived?.actionGroups?.count, 1)
98+
XCTAssertEqual(unarchived?.actionGroups?.first?.name, group.name)
99+
XCTAssertEqual(unarchived?.actionGroups?.first?.actions.first?.name, action.name)
100+
}
101+
}

libs/SalesforceSDKCore/SalesforceSDKCoreTests/PushNotificationManagerTests.swift

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -538,10 +538,19 @@ class PushNotificationManagerTests: XCTestCase {
538538
// When
539539
try await pushNotificationManager.fetchAndStoreNotificationTypes(restClient: mockRestClient)
540540

541+
// ** Archive and unarchive the user account to test NSSecureCoding **//
542+
let data = try NSKeyedArchiver.archivedData(withRootObject: mockUserAccount!, requiringSecureCoding: true)
543+
let unarchivedAccount = try NSKeyedUnarchiver.unarchivedObject(ofClass: UserAccount.self, from: data)
544+
541545
// Then
542546
XCTAssertNotNil(mockUserAccount.notificationTypes)
543547
XCTAssertEqual(mockUserAccount.notificationTypes?.count, 11)
544548

549+
550+
// ** Assert notificationTypes are preserved ** //
551+
XCTAssertNotNil(unarchivedAccount?.notificationTypes)
552+
XCTAssertEqual(unarchivedAccount?.notificationTypes?.count, mockUserAccount.notificationTypes?.count)
553+
XCTAssertEqual(unarchivedAccount?.notificationTypes?.first?.apiName, mockUserAccount.notificationTypes?.first?.apiName)
545554
}
546555

547556
func testFetchAndStoreNotificationTypes_NoAccount() async {
@@ -957,63 +966,6 @@ class NotificationCategoryFactoryTests: XCTestCase {
957966
}
958967
}
959968

960-
class ActionTypeTests: XCTestCase {
961-
962-
func testActionTypeDecoding() throws {
963-
// Given
964-
let json = """
965-
{
966-
"name": "test",
967-
"actionKey": "test_key",
968-
"label": "Test Label",
969-
"type": "NotificationApiAction"
970-
}
971-
"""
972-
let jsonData = json.data(using: .utf8)!
973-
974-
// When
975-
let action = try JSONDecoder().decode(Action.self, from: jsonData)
976-
977-
// Then
978-
XCTAssertEqual(action.type, "NotificationApiAction")
979-
}
980-
981-
func testActionTypeDecodingForeground() throws {
982-
// Given
983-
let json = """
984-
{
985-
"name": "test",
986-
"actionKey": "test_key",
987-
"label": "Test Label",
988-
"type": "foreground"
989-
}
990-
"""
991-
let jsonData = json.data(using: .utf8)!
992-
993-
// When
994-
let action = try JSONDecoder().decode(Action.self, from: jsonData)
995-
996-
// Then
997-
XCTAssertEqual(action.type, "foreground")
998-
}
999-
1000-
func testActionTypeDecodingInvalidType() throws {
1001-
// Given
1002-
let json = """
1003-
{
1004-
"name": "test",
1005-
"actionKey": "test_key",
1006-
"label": "Test Label",
1007-
"invalidType": "invalidType"
1008-
}
1009-
"""
1010-
let jsonData = json.data(using: .utf8)!
1011-
1012-
// When, Then
1013-
XCTAssertThrowsError(try JSONDecoder().decode(Action.self, from: jsonData))
1014-
}
1015-
}
1016-
1017969
// MARK: - Mocks
1018970
class MockApplicationHelper: RemoteNotificationRegistering {
1019971
var client: RestClient?

0 commit comments

Comments
 (0)