Skip to content

Commit a87f4a8

Browse files
authored
Merge pull request #991 from OneSignal/feature/notif_action_icons
iOS 15 Feature Notification action icons
2 parents 8d6c6ce + dda1993 commit a87f4a8

File tree

4 files changed

+174
-7
lines changed

4 files changed

+174
-7
lines changed

iOS_SDK/OneSignalSDK/Source/OSNotification.m

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,20 @@ - (void)parseActionButtons:(NSArray<NSDictionary*>*)buttons {
169169
continue;
170170
}
171171

172-
[buttonArray addObject: @{
173-
@"text" : button[@"n"],
174-
@"id" : (button[@"i"] ?: button[@"n"])
175-
}];
172+
NSMutableDictionary *actionDict = [NSMutableDictionary new];
173+
actionDict[@"text"] = button[@"n"];
174+
actionDict[@"id"] = button[@"i"] ?: button[@"n"];
175+
176+
// Parse Action Icon into system or template icon
177+
if (button[@"icon_type"] && button[@"path"]) {
178+
if ([button[@"icon_type"] isEqualToString:@"system"]) {
179+
actionDict[@"systemIcon"] = button[@"path"];
180+
} else if ([button[@"icon_type"] isEqualToString:@"custom"]) {
181+
actionDict[@"templateIcon"] = button[@"path"];
182+
}
183+
}
184+
185+
[buttonArray addObject: actionDict];
176186
}
177187

178188
_actionButtons = buttonArray;

iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,16 +566,59 @@ + (UNNotificationRequest*)prepareUNNotificationRequest:(OSNotification*)notifica
566566
return [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
567567
}
568568

569+
+ (UNNotificationAction *)createActionForButton:(NSDictionary *)button {
570+
NSString *buttonId = button[@"id"];
571+
NSString *buttonText = button[@"text"];
572+
573+
if (@available(iOS 15.0, *)) {
574+
// Using reflection for Xcode versions lower than 13
575+
id icon; // UNNotificationActionIcon
576+
let UNNotificationActionIconClass = NSClassFromString(@"UNNotificationActionIcon");
577+
if (UNNotificationActionIconClass) {
578+
if (button[@"systemIcon"]) {
579+
icon = [UNNotificationActionIconClass performSelector:@selector(iconWithSystemImageName:)
580+
withObject:button[@"systemIcon"]];
581+
} else if (button[@"templateIcon"]) {
582+
icon = [UNNotificationActionIconClass performSelector:@selector(iconWithTemplateImageName:)
583+
withObject:button[@"templateIcon"]];
584+
}
585+
}
586+
587+
// We need to use NSInvocation because performSelector only allows up to 2 arguments
588+
SEL actionSelector = NSSelectorFromString(@"actionWithIdentifier:title:options:icon:");
589+
UNNotificationAction * __unsafe_unretained action;
590+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UNNotificationAction methodSignatureForSelector:actionSelector]];
591+
[invocation setTarget:[UNNotificationAction class]];
592+
[invocation setSelector:actionSelector];
593+
/*
594+
From Apple's Documentation on NSInvocation:
595+
Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively;
596+
you should set these values directly with the target and selector properties.
597+
Use indices 2 and greater for the arguments normally passed in a message.
598+
*/
599+
NSUInteger actionOption = UNNotificationActionOptionForeground;
600+
[invocation setArgument:&buttonId atIndex:2];
601+
[invocation setArgument:&buttonText atIndex:3];
602+
[invocation setArgument:&actionOption atIndex:4];
603+
[invocation setArgument:&icon atIndex:5];
604+
[invocation invoke];
605+
[invocation getReturnValue:&action];
606+
return action;
607+
} else {
608+
return [UNNotificationAction actionWithIdentifier:buttonId
609+
title:buttonText
610+
options:UNNotificationActionOptionForeground];
611+
}
612+
}
613+
569614
+ (void)addActionButtons:(OSNotification*)notification
570615
toNotificationContent:(UNMutableNotificationContent*)content {
571616
if (!notification.actionButtons || notification.actionButtons.count == 0)
572617
return;
573618

574619
let actionArray = [NSMutableArray new];
575620
for(NSDictionary* button in notification.actionButtons) {
576-
let action = [UNNotificationAction actionWithIdentifier:button[@"id"]
577-
title:button[@"text"]
578-
options:UNNotificationActionOptionForeground];
621+
let action = [self createActionForButton:button];
579622
[actionArray addObject:action];
580623
}
581624

iOS_SDK/OneSignalSDK/UnitTests/Shadows/OneSignalHelperOverrider.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
#import <Foundation/Foundation.h>
2929
#import <UIKit/UIKit.h>
3030
#import <XCTest/XCTest.h>
31+
#import "OneSignalHelper.h"
32+
33+
@interface OneSignalHelper (Tests)
34+
+ (UNNotificationAction *)createActionForButton:(NSDictionary *)button;
35+
@end
3136

3237
@interface OneSignalHelperOverrider : NSObject
3338
+ (void)reset;

iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3108,6 +3108,115 @@ - (void)testDeviceStateJson {
31083108
XCTAssertEqualObjects(json[@"isSMSSubscribed"], @1);
31093109
}
31103110

3111+
- (void)testParseNotificationSystemActionIconJson {
3112+
NSDictionary *aps = @{
3113+
@"aps": @{
3114+
@"content-available": @1,
3115+
@"mutable-content": @1,
3116+
@"alert": @"Message Body",
3117+
},
3118+
@"os_data": @{
3119+
@"i": @"notif id",
3120+
@"ti": @"templateId123",
3121+
@"tn": @"Template name",
3122+
@"buttons": @[@{
3123+
@"i": @"id1",
3124+
@"n": @"text1",
3125+
@"path": @"hand.thumbsup",
3126+
@"icon_type": @"system"
3127+
}]
3128+
}};
3129+
OSNotification *notification = [OSNotification parseWithApns:aps];
3130+
XCTAssertEqualObjects(notification.actionButtons[0][@"systemIcon"], @"hand.thumbsup");
3131+
}
3132+
3133+
- (void)testParseNotificationTemplateActionIconJson {
3134+
NSDictionary *aps = @{
3135+
@"aps": @{
3136+
@"content-available": @1,
3137+
@"mutable-content": @1,
3138+
@"alert": @"Message Body",
3139+
},
3140+
@"os_data": @{
3141+
@"i": @"notif id",
3142+
@"ti": @"templateId123",
3143+
@"tn": @"Template name",
3144+
@"buttons": @[@{
3145+
@"i": @"id1",
3146+
@"n": @"text1",
3147+
@"path": @"myImage/thumbsup",
3148+
@"icon_type": @"custom"
3149+
}]
3150+
}};
3151+
OSNotification *notification = [OSNotification parseWithApns:aps];
3152+
XCTAssertEqualObjects(notification.actionButtons[0][@"templateIcon"], @"myImage/thumbsup");
3153+
}
3154+
3155+
- (void)testCreateActionForButtonsWithIcon {
3156+
if (@available(iOS 15.0, *)) {
3157+
NSDictionary *aps = @{
3158+
@"aps": @{
3159+
@"content-available": @1,
3160+
@"mutable-content": @1,
3161+
@"alert": @"Message Body",
3162+
},
3163+
@"os_data": @{
3164+
@"i": @"notif id",
3165+
@"ti": @"templateId123",
3166+
@"tn": @"Template name",
3167+
@"buttons": @[@{
3168+
@"i": @"id1",
3169+
@"n": @"text1",
3170+
@"path": @"myImage/thumbsup",
3171+
@"icon_type": @"custom"
3172+
}]
3173+
}};
3174+
OSNotification *notification = [OSNotification parseWithApns:aps];
3175+
UNNotificationAction *action = [OneSignalHelper createActionForButton:notification.actionButtons[0]];
3176+
XCTAssertNotNil(action.icon);
3177+
3178+
aps = @{
3179+
@"aps": @{
3180+
@"content-available": @1,
3181+
@"mutable-content": @1,
3182+
@"alert": @"Message Body",
3183+
},
3184+
@"os_data": @{
3185+
@"i": @"notif id",
3186+
@"ti": @"templateId123",
3187+
@"tn": @"Template name",
3188+
@"buttons": @[@{
3189+
@"i": @"id1",
3190+
@"n": @"text1",
3191+
@"path": @"hand.thumbsup",
3192+
@"icon_type": @"system"
3193+
}]
3194+
}};
3195+
notification = [OSNotification parseWithApns:aps];
3196+
action = [OneSignalHelper createActionForButton:notification.actionButtons[0]];
3197+
XCTAssertNotNil(action.icon);
3198+
3199+
aps = @{
3200+
@"aps": @{
3201+
@"content-available": @1,
3202+
@"mutable-content": @1,
3203+
@"alert": @"Message Body",
3204+
},
3205+
@"os_data": @{
3206+
@"i": @"notif id",
3207+
@"ti": @"templateId123",
3208+
@"tn": @"Template name",
3209+
@"buttons": @[@{
3210+
@"i": @"id1",
3211+
@"n": @"text1"
3212+
}]
3213+
}};
3214+
notification = [OSNotification parseWithApns:aps];
3215+
action = [OneSignalHelper createActionForButton:notification.actionButtons[0]];
3216+
XCTAssertNil(action.icon);
3217+
}
3218+
}
3219+
31113220
- (void)testNotificationJson {
31123221
NSDictionary *aps = @{
31133222
@"aps": @{

0 commit comments

Comments
 (0)