Skip to content

Commit a02c244

Browse files
committed
Payload parsing refactor and firebase tests
* Refactored internal payload parsing in OSNotificationPayload * Added tests for firebase tracking of opened, received, and influenced events. * Fixed handling of short titles for firebase events.
1 parent 62f03c6 commit a02c244

File tree

8 files changed

+363
-172
lines changed

8 files changed

+363
-172
lines changed

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
03389F691FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 03389F681FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m */; };
1011
1AF75EAE1E8567FD0097B315 /* NSString+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF75EAD1E8567FD0097B315 /* NSString+OneSignal.m */; };
1112
1AF75EAF1E8569710097B315 /* NSString+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF75EAD1E8567FD0097B315 /* NSString+OneSignal.m */; };
1213
1AF75EB01E8569720097B315 /* NSString+OneSignal.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF75EAD1E8567FD0097B315 /* NSString+OneSignal.m */; };
@@ -146,6 +147,8 @@
146147
/* End PBXCopyFilesBuildPhase section */
147148

148149
/* Begin PBXFileReference section */
150+
03389F671FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalTrackFirebaseAnalyticsOverrider.h; sourceTree = "<group>"; };
151+
03389F681FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalTrackFirebaseAnalyticsOverrider.m; sourceTree = "<group>"; };
149152
1AF75EAC1E8567FD0097B315 /* NSString+OneSignal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+OneSignal.h"; sourceTree = "<group>"; };
150153
1AF75EAD1E8567FD0097B315 /* NSString+OneSignal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+OneSignal.m"; sourceTree = "<group>"; };
151154
37747F9319147D6500558FAD /* libOneSignal.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libOneSignal.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -334,6 +337,8 @@
334337
4529DEEF1FA8433500CEAB1D /* NSLocaleOverrider.m */,
335338
4529DEF11FA8440A00CEAB1D /* UIAlertViewOverrider.h */,
336339
4529DEF21FA8440A00CEAB1D /* UIAlertViewOverrider.m */,
340+
03389F671FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.h */,
341+
03389F681FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m */,
337342
);
338343
path = Shadows;
339344
sourceTree = "<group>";
@@ -695,6 +700,7 @@
695700
912412341E73342200E41FD7 /* OneSignalTracker.m in Sources */,
696701
912412101E73342200E41FD7 /* OneSignal.m in Sources */,
697702
9124122C1E73342200E41FD7 /* OneSignalReachability.m in Sources */,
703+
03389F691FB548A0006537F0 /* OneSignalTrackFirebaseAnalyticsOverrider.m in Sources */,
698704
4529DED51FA823B900CEAB1D /* TestHelperFunctions.m in Sources */,
699705
911E2CBD1E398AB3003112A4 /* UnitTests.m in Sources */,
700706
91B6EA431E85D38F00B5CF01 /* OSObservable.m in Sources */,

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/UnitTests.xcscheme

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@
5151
debugServiceExtension = "internal"
5252
allowLocationSimulation = "YES">
5353
<AdditionalOptions>
54-
<AdditionalOption
55-
key = "MallocScribble"
56-
value = ""
57-
isEnabled = "YES">
58-
</AdditionalOption>
5954
</AdditionalOptions>
6055
</LaunchAction>
6156
<ProfileAction

iOS_SDK/OneSignalSDK/Source/OSNotificationPayload.m

Lines changed: 114 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -32,102 +32,124 @@
3232
#import "OneSignal.h"
3333

3434
@implementation OSNotificationPayload
35-
@synthesize actionButtons = _actionButtons, additionalData = _additionalData, badge = _badge, body = _body, contentAvailable = _contentAvailable, notificationID = _notificationID, launchURL = _launchURL, rawPayload = _rawPayload, sound = _sound, subtitle = _subtitle, title = _title, attachments = _attachments;
3635

3736
- (id)initWithRawMessage:(NSDictionary*)message {
37+
if (!message)
38+
return nil;
39+
3840
self = [super init];
39-
if (self && message) {
40-
_rawPayload = [NSDictionary dictionaryWithDictionary:message];
41-
42-
BOOL is2dot4Format = [_rawPayload[@"os_data"][@"buttons"] isKindOfClass:[NSArray class]];
43-
44-
if (_rawPayload[@"aps"][@"content-available"])
45-
_contentAvailable = (BOOL)_rawPayload[@"aps"][@"content-available"];
46-
else
47-
_contentAvailable = NO;
48-
49-
if (_rawPayload[@"aps"][@"badge"])
50-
_badge = [_rawPayload[@"aps"][@"badge"] intValue];
51-
else
52-
_badge = [_rawPayload[@"badge"] intValue];
53-
54-
_actionButtons = _rawPayload[@"o"];
55-
if (!_actionButtons) {
56-
if (is2dot4Format)
57-
_actionButtons = _rawPayload[@"os_data"][@"buttons"];
58-
else
59-
_actionButtons = _rawPayload[@"os_data"][@"buttons"][@"o"];
60-
}
61-
62-
if(_rawPayload[@"aps"][@"sound"])
63-
_sound = _rawPayload[@"aps"][@"sound"];
64-
else if(_rawPayload[@"s"])
65-
_sound = _rawPayload[@"s"];
66-
else if (!is2dot4Format)
67-
_sound = _rawPayload[@"os_data"][@"buttons"][@"s"];
68-
69-
if(_rawPayload[@"custom"]) {
70-
NSDictionary* custom = _rawPayload[@"custom"];
71-
if (custom[@"a"])
72-
_additionalData = [custom[@"a"] copy];
73-
_notificationID = custom[@"i"];
74-
_launchURL = custom[@"u"];
75-
76-
_attachments = [_rawPayload[@"at"] copy];
77-
}
78-
else if(_rawPayload[@"os_data"]) {
79-
NSDictionary * os_data = _rawPayload[@"os_data"];
80-
81-
NSMutableDictionary *additional = [_rawPayload mutableCopy];
82-
[additional removeObjectForKey:@"aps"];
83-
[additional removeObjectForKey:@"os_data"];
84-
_additionalData = [[NSDictionary alloc] initWithDictionary:additional];
85-
86-
_notificationID = os_data[@"i"];
87-
_launchURL = os_data[@"u"];
88-
89-
if (is2dot4Format) {
90-
if (os_data[@"att"])
91-
_attachments = [os_data[@"att"] copy];
92-
}
93-
else {
94-
if (os_data[@"buttons"][@"at"])
95-
_attachments = [os_data[@"buttons"][@"at"] copy];
96-
}
97-
}
98-
99-
if(_rawPayload[@"m"]) {
100-
id m = _rawPayload[@"m"];
101-
if ([m isKindOfClass:[NSDictionary class]]) {
102-
_body = m[@"body"];
103-
_title = m[@"title"];
104-
_subtitle = m[@"subtitle"];
105-
}
106-
else
107-
_body = m;
108-
}
109-
else if(_rawPayload[@"aps"][@"alert"]) {
110-
id a = message[@"aps"][@"alert"];
111-
if ([a isKindOfClass:[NSDictionary class]]) {
112-
_body = a[@"body"];
113-
_title = a[@"title"];
114-
_subtitle = a[@"subtitle"];
115-
}
116-
else
117-
_body = a;
118-
}
119-
else if(_rawPayload[@"os_data"][@"buttons"][@"m"]) {
120-
id m = _rawPayload[@"os_data"][@"buttons"][@"m"];
121-
if ([m isKindOfClass:[NSDictionary class]]) {
122-
_body = m[@"body"];
123-
_title = m[@"title"];
124-
_subtitle = m[@"subtitle"];
125-
}
126-
else
127-
_body = m;
128-
}
129-
}
41+
if (!self)
42+
return nil;
43+
44+
_rawPayload = [NSDictionary dictionaryWithDictionary:message];
45+
46+
if ([_rawPayload[@"os_data"] isKindOfClass:[NSDictionary class]])
47+
[self parseOSDataPayload];
48+
else
49+
[self parseOriginalPayload];
50+
51+
[self parseOtherApnsFields];
13052

13153
return self;
13254
}
55+
56+
// Original OneSignal payload format.
57+
-(void)parseOriginalPayload {
58+
BOOL remoteSlient = _rawPayload[@"m"] && !_rawPayload[@"aps"][@"alert"];
59+
if (remoteSlient)
60+
[self parseRemoteSlient:_rawPayload];
61+
else {
62+
[self parseApnsFields];
63+
_attachments = _rawPayload[@"at"];
64+
[self parseActionButtons:_rawPayload[@"o"]];
65+
}
66+
67+
[self parseCommonOneSignalFields:_rawPayload[@"custom"]];
68+
_additionalData = _rawPayload[@"custom"][@"a"];
69+
}
70+
71+
// New OneSignal playload format.
72+
// OneSignal specific features are under "os_data".
73+
-(void)parseOSDataPayload {
74+
NSDictionary *os_data = _rawPayload[@"os_data"];
75+
BOOL remoteSlient = os_data[@"buttons"] && !_rawPayload[@"aps"][@"alert"];
76+
if (remoteSlient)
77+
[self parseRemoteSlient:os_data[@"buttons"]];
78+
else {
79+
[self parseApnsFields];
80+
_attachments = os_data[@"att"];
81+
[self parseActionButtons:os_data[@"buttons"]];
82+
}
83+
84+
[self parseCommonOneSignalFields:_rawPayload[@"os_data"]];
85+
[self parseOSDataAdditionalData];
86+
}
87+
88+
-(void)parseOSDataAdditionalData {
89+
NSMutableDictionary *additional = [_rawPayload mutableCopy];
90+
[additional removeObjectForKey:@"aps"];
91+
[additional removeObjectForKey:@"os_data"];
92+
_additionalData = [[NSDictionary alloc] initWithDictionary:additional];
93+
}
94+
95+
// Fields that share the same format for all OneSignal payload types.
96+
-(void)parseCommonOneSignalFields:(NSDictionary*)payload {
97+
_notificationID = payload[@"i"];
98+
_launchURL = payload[@"u"];
99+
_templateID = payload[@"ti"];
100+
_templateName = payload[@"tn"];
101+
}
102+
103+
-(void)parseApnsFields {
104+
[self parseAlertField:_rawPayload[@"aps"][@"alert"]];
105+
_badge = [_rawPayload[@"aps"][@"badge"] intValue];
106+
_sound = _rawPayload[@"aps"][@"sound"];
107+
}
108+
109+
// Pasrse the APNs alert field, can be a NSString or a NSDictionary
110+
-(void)parseAlertField:(NSObject*)alert {
111+
if ([alert isKindOfClass:[NSDictionary class]]) {
112+
NSDictionary *alertDictionary = (NSDictionary*)alert;
113+
_body = alertDictionary[@"body"];
114+
_title = alertDictionary[@"title"];
115+
_subtitle = alertDictionary[@"subtitle"];
116+
}
117+
else
118+
_body = (NSString*)alert;
119+
}
120+
121+
// Only used on iOS 9 and older.
122+
// - Or if the OneSignal server hasn't received the iOS version update.
123+
// May also be used if OneSignal server hasn't received the SDK version 2.4.0+ update event
124+
-(void)parseRemoteSlient:(NSDictionary*)payload {
125+
[self parseAlertField:payload[@"m"]];
126+
_badge = [payload[@"b"] intValue];
127+
_sound = payload[@"s"];
128+
_attachments = payload[@"at"];
129+
[self parseActionButtons:_rawPayload[@"o"]];
130+
}
131+
132+
// Parse and convert minified keys for action buttons
133+
-(void)parseActionButtons:(NSArray<NSDictionary*>*)buttons {
134+
NSMutableArray *buttonArray = [NSMutableArray new];
135+
for (NSDictionary *button in buttons) {
136+
[buttonArray addObject: @{@"text" : button[@"n"],
137+
@"id" : (button[@"i"] ?: button[@"n"])
138+
}];
139+
}
140+
141+
_actionButtons = buttonArray;
142+
}
143+
144+
-(void)parseOtherApnsFields {
145+
NSDictionary *aps = _rawPayload[@"aps"];
146+
if (aps[@"content-available"])
147+
_contentAvailable = (BOOL)aps[@"content-available"];
148+
149+
if (aps[@"mutable-content"])
150+
_mutableContent = (BOOL)aps[@"mutable-content"];
151+
152+
_category = aps[@"category"];
153+
}
154+
133155
@end

iOS_SDK/OneSignalSDK/Source/OneSignal.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,28 @@ typedef NS_ENUM(NSUInteger, OSNotificationDisplayType) {
9393
/* Name of Template */
9494
@property(readonly)NSString* templateName;
9595

96-
/* Provide this key with a value of 1 to indicate that new content is available.
97-
Including this key and value means that when your app is launched in the background or resumed application:didReceiveRemoteNotification:fetchCompletionHandler: is called. */
96+
/* True when the key content-available is set to 1 in the aps payload.
97+
content-available is used to wake your app when the payload is received.
98+
See Apple's documenation for more details.
99+
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application
100+
*/
98101
@property(readonly)BOOL contentAvailable;
99102

103+
/* True when the key mutable-content is set to 1 in the aps payload.
104+
mutable-content is used to wake your Notification Service Extension to modify a notification.
105+
See Apple's documenation for more details.
106+
https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension
107+
*/
108+
@property(readonly)BOOL mutableContent;
109+
110+
/*
111+
Notification category key previously registered to display with.
112+
This overrides OneSignal's actionButtons.
113+
See Apple's documenation for more details.
114+
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SupportingNotificationsinYourApp.html#//apple_ref/doc/uid/TP40008194-CH4-SW26
115+
*/
116+
@property(readonly)NSString* category;
117+
100118
/* The badge assigned to the application icon */
101119
@property(readonly)NSUInteger badge;
102120

@@ -127,6 +145,7 @@ typedef NS_ENUM(NSUInteger, OSNotificationDisplayType) {
127145

128146
@end
129147

148+
// ## OneSignal OSNotification
130149
@interface OSNotification : NSObject
131150

132151
/* Notification Payload */

iOS_SDK/OneSignalSDK/Source/OneSignalTrackFirebaseAnalytics.m

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727

2828
#import "OneSignalTrackFirebaseAnalytics.h"
2929

30-
//#import <FirebaseAnalytics/FIRAnalytics.h>
31-
3230
@implementation OneSignalTrackFirebaseAnalytics
3331

3432
static NSTimeInterval lastOpenedTime = 0;
@@ -53,7 +51,12 @@ +(NSString*)getCampaignNameFromPayload:(OSNotificationPayload*)payload {
5351
return [NSString stringWithFormat:@"%@ - %@", payload.templateName, payload.templateID];
5452
if (!payload.title)
5553
return @"";
56-
return [payload.title substringToIndex:10];
54+
55+
int titleLength = payload.title.length;
56+
if (titleLength > 10)
57+
titleLength = 10;
58+
59+
return [payload.title substringToIndex:titleLength];
5760
}
5861

5962
+(void)trackOpenEvent:(OSNotificationOpenedResult*)results {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2017 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#import <Foundation/Foundation.h>
29+
30+
@interface OneSignalTrackFirebaseAnalyticsOverrider : NSObject
31+
+(void)reset;
32+
+(NSArray<NSDictionary*>*)loggedEvents;
33+
@end

0 commit comments

Comments
 (0)