diff --git a/iOS_SDK/OneSignalSDK/Source/OSNotification.m b/iOS_SDK/OneSignalSDK/Source/OSNotification.m index c62127922..6db346f56 100644 --- a/iOS_SDK/OneSignalSDK/Source/OSNotification.m +++ b/iOS_SDK/OneSignalSDK/Source/OSNotification.m @@ -169,10 +169,20 @@ - (void)parseActionButtons:(NSArray*)buttons { continue; } - [buttonArray addObject: @{ - @"text" : button[@"n"], - @"id" : (button[@"i"] ?: button[@"n"]) - }]; + NSMutableDictionary *actionDict = [NSMutableDictionary new]; + actionDict[@"text"] = button[@"n"]; + actionDict[@"id"] = button[@"i"] ?: button[@"n"]; + + // Parse Action Icon into system or template icon + if (button[@"icon_type"] && button[@"path"]) { + if ([button[@"icon_type"] isEqualToString:@"system"]) { + actionDict[@"systemIcon"] = button[@"path"]; + } else if ([button[@"icon_type"] isEqualToString:@"custom"]) { + actionDict[@"templateIcon"] = button[@"path"]; + } + } + + [buttonArray addObject: actionDict]; } _actionButtons = buttonArray; diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m b/iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m index 504d22387..f773afa61 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m @@ -566,6 +566,51 @@ + (UNNotificationRequest*)prepareUNNotificationRequest:(OSNotification*)notifica return [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; } ++ (UNNotificationAction *)createActionForButton:(NSDictionary *)button { + NSString *buttonId = button[@"id"]; + NSString *buttonText = button[@"text"]; + + if (@available(iOS 15.0, *)) { + // Using reflection for Xcode versions lower than 13 + id icon; // UNNotificationActionIcon + let UNNotificationActionIconClass = NSClassFromString(@"UNNotificationActionIcon"); + if (UNNotificationActionIconClass) { + if (button[@"systemIcon"]) { + icon = [UNNotificationActionIconClass performSelector:@selector(iconWithSystemImageName:) + withObject:button[@"systemIcon"]]; + } else if (button[@"templateIcon"]) { + icon = [UNNotificationActionIconClass performSelector:@selector(iconWithTemplateImageName:) + withObject:button[@"templateIcon"]]; + } + } + + // We need to use NSInvocation because performSelector only allows up to 2 arguments + SEL actionSelector = NSSelectorFromString(@"actionWithIdentifier:title:options:icon:"); + UNNotificationAction * __unsafe_unretained action; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UNNotificationAction methodSignatureForSelector:actionSelector]]; + [invocation setTarget:[UNNotificationAction class]]; + [invocation setSelector:actionSelector]; + /* + From Apple's Documentation on NSInvocation: + Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively; + you should set these values directly with the target and selector properties. + Use indices 2 and greater for the arguments normally passed in a message. + */ + NSUInteger actionOption = UNNotificationActionOptionForeground; + [invocation setArgument:&buttonId atIndex:2]; + [invocation setArgument:&buttonText atIndex:3]; + [invocation setArgument:&actionOption atIndex:4]; + [invocation setArgument:&icon atIndex:5]; + [invocation invoke]; + [invocation getReturnValue:&action]; + return action; + } else { + return [UNNotificationAction actionWithIdentifier:buttonId + title:buttonText + options:UNNotificationActionOptionForeground]; + } +} + + (void)addActionButtons:(OSNotification*)notification toNotificationContent:(UNMutableNotificationContent*)content { if (!notification.actionButtons || notification.actionButtons.count == 0) @@ -573,9 +618,7 @@ + (void)addActionButtons:(OSNotification*)notification let actionArray = [NSMutableArray new]; for(NSDictionary* button in notification.actionButtons) { - let action = [UNNotificationAction actionWithIdentifier:button[@"id"] - title:button[@"text"] - options:UNNotificationActionOptionForeground]; + let action = [self createActionForButton:button]; [actionArray addObject:action]; } diff --git a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalHelperOverrider.h b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalHelperOverrider.h index eec211832..73f337bf8 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalHelperOverrider.h +++ b/iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalHelperOverrider.h @@ -28,6 +28,11 @@ #import #import #import +#import "OneSignalHelper.h" + +@interface OneSignalHelper (Tests) ++ (UNNotificationAction *)createActionForButton:(NSDictionary *)button; +@end @interface OneSignalHelperOverrider : NSObject + (void)reset; diff --git a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m index 412222759..1571ae56f 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m @@ -3113,6 +3113,115 @@ - (void)testDeviceStateJson { XCTAssertEqualObjects(json[@"isSMSSubscribed"], @1); } +- (void)testParseNotificationSystemActionIconJson { + NSDictionary *aps = @{ + @"aps": @{ + @"content-available": @1, + @"mutable-content": @1, + @"alert": @"Message Body", + }, + @"os_data": @{ + @"i": @"notif id", + @"ti": @"templateId123", + @"tn": @"Template name", + @"buttons": @[@{ + @"i": @"id1", + @"n": @"text1", + @"path": @"hand.thumbsup", + @"icon_type": @"system" + }] + }}; + OSNotification *notification = [OSNotification parseWithApns:aps]; + XCTAssertEqualObjects(notification.actionButtons[0][@"systemIcon"], @"hand.thumbsup"); +} + +- (void)testParseNotificationTemplateActionIconJson { + NSDictionary *aps = @{ + @"aps": @{ + @"content-available": @1, + @"mutable-content": @1, + @"alert": @"Message Body", + }, + @"os_data": @{ + @"i": @"notif id", + @"ti": @"templateId123", + @"tn": @"Template name", + @"buttons": @[@{ + @"i": @"id1", + @"n": @"text1", + @"path": @"myImage/thumbsup", + @"icon_type": @"custom" + }] + }}; + OSNotification *notification = [OSNotification parseWithApns:aps]; + XCTAssertEqualObjects(notification.actionButtons[0][@"templateIcon"], @"myImage/thumbsup"); +} + +- (void)testCreateActionForButtonsWithIcon { + if (@available(iOS 15.0, *)) { + NSDictionary *aps = @{ + @"aps": @{ + @"content-available": @1, + @"mutable-content": @1, + @"alert": @"Message Body", + }, + @"os_data": @{ + @"i": @"notif id", + @"ti": @"templateId123", + @"tn": @"Template name", + @"buttons": @[@{ + @"i": @"id1", + @"n": @"text1", + @"path": @"myImage/thumbsup", + @"icon_type": @"custom" + }] + }}; + OSNotification *notification = [OSNotification parseWithApns:aps]; + UNNotificationAction *action = [OneSignalHelper createActionForButton:notification.actionButtons[0]]; + XCTAssertNotNil(action.icon); + + aps = @{ + @"aps": @{ + @"content-available": @1, + @"mutable-content": @1, + @"alert": @"Message Body", + }, + @"os_data": @{ + @"i": @"notif id", + @"ti": @"templateId123", + @"tn": @"Template name", + @"buttons": @[@{ + @"i": @"id1", + @"n": @"text1", + @"path": @"hand.thumbsup", + @"icon_type": @"system" + }] + }}; + notification = [OSNotification parseWithApns:aps]; + action = [OneSignalHelper createActionForButton:notification.actionButtons[0]]; + XCTAssertNotNil(action.icon); + + aps = @{ + @"aps": @{ + @"content-available": @1, + @"mutable-content": @1, + @"alert": @"Message Body", + }, + @"os_data": @{ + @"i": @"notif id", + @"ti": @"templateId123", + @"tn": @"Template name", + @"buttons": @[@{ + @"i": @"id1", + @"n": @"text1" + }] + }}; + notification = [OSNotification parseWithApns:aps]; + action = [OneSignalHelper createActionForButton:notification.actionButtons[0]]; + XCTAssertNil(action.icon); + } +} + - (void)testNotificationJson { NSDictionary *aps = @{ @"aps": @{