Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS 15 Feature Notification action icons #991

Merged
merged 5 commits into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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": @{
Expand Down