Skip to content

Commit

Permalink
Merge pull request #991 from OneSignal/feature/notif_action_icons
Browse files Browse the repository at this point in the history
iOS 15 Feature Notification action icons
  • Loading branch information
emawby authored Sep 13, 2021
2 parents 8d6c6ce + dda1993 commit a87f4a8
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 7 deletions.
18 changes: 14 additions & 4 deletions iOS_SDK/OneSignalSDK/Source/OSNotification.m
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,20 @@ - (void)parseActionButtons:(NSArray<NSDictionary*>*)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;
Expand Down
49 changes: 46 additions & 3 deletions iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -566,16 +566,59 @@ + (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)
return;

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];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "OneSignalHelper.h"

@interface OneSignalHelper (Tests)
+ (UNNotificationAction *)createActionForButton:(NSDictionary *)button;
@end

@interface OneSignalHelperOverrider : NSObject
+ (void)reset;
Expand Down
109 changes: 109 additions & 0 deletions iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -3108,6 +3108,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": @{
Expand Down

0 comments on commit a87f4a8

Please sign in to comment.