-
Notifications
You must be signed in to change notification settings - Fork 263
/
OSMessagingController.m
1129 lines (950 loc) · 51.7 KB
/
OSMessagingController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Modified MIT License
*
* Copyright 2017 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "OSMessagingController.h"
#import "UIApplication+OneSignal.h" // Previously imported via "OneSignalHelper.h"
#import "NSDateFormatter+OneSignal.h" // Previously imported via "OneSignalHelper.h"
#import <OneSignalCore/OneSignalCore.h>
#import "OSInAppMessageClickResult.h"
#import "OSInAppMessageClickEvent.h"
#import "OSInAppMessageController.h"
#import "OSInAppMessagePrompt.h"
#import "OSInAppMessagingRequests.h"
#import "OneSignalWebViewManager.h"
#import <OneSignalOutcomes/OneSignalOutcomes.h>
#import "OSSessionManager.h"
@implementation OSInAppMessageWillDisplayEvent
- (OSInAppMessageWillDisplayEvent*)initWithInAppMessage:(OSInAppMessage *)message {
_message = message;
return self;
}
- (NSDictionary *)jsonRepresentation {
NSMutableDictionary *json = [NSMutableDictionary new];
json[@"message"] = [self.message jsonRepresentation];
return json;
}
@end
@implementation OSInAppMessageDidDisplayEvent
- (OSInAppMessageDidDisplayEvent*)initWithInAppMessage:(OSInAppMessage *)message {
_message = message;
return self;
}
- (NSDictionary *)jsonRepresentation {
NSMutableDictionary *json = [NSMutableDictionary new];
json[@"message"] = [self.message jsonRepresentation];
return json;
}
@end
@implementation OSInAppMessageWillDismissEvent
- (OSInAppMessageWillDismissEvent*)initWithInAppMessage:(OSInAppMessage *)message {
_message = message;
return self;
}
- (NSDictionary *)jsonRepresentation {
NSMutableDictionary *json = [NSMutableDictionary new];
json[@"message"] = [self.message jsonRepresentation];
return json;
}
@end
@implementation OSInAppMessageDidDismissEvent
- (OSInAppMessageDidDismissEvent*)initWithInAppMessage:(OSInAppMessage *)message {
_message = message;
return self;
}
- (NSDictionary *)jsonRepresentation {
NSMutableDictionary *json = [NSMutableDictionary new];
json[@"message"] = [self.message jsonRepresentation];
return json;
}
@end
@interface OSMessagingController ()
@property (strong, nonatomic, nullable) UIWindow *window;
@property (strong, nonatomic, nonnull) NSArray <OSInAppMessageInternal *> *messages;
@property (strong, nonatomic, nonnull) OSTriggerController *triggerController;
@property (strong, nonatomic, nonnull) NSMutableArray <OSInAppMessageInternal *> *messageDisplayQueue;
// Tracking already seen IAMs, used to prevent showing an IAM more than once after it has been dismissed
@property (strong, nonatomic, nonnull) NSMutableSet <NSString *> *seenInAppMessages;
// Tracking IAMs with redisplay, used to enable showing an IAM more than once after it has been dismissed
@property (strong, nonatomic, nonnull) NSMutableDictionary <NSString *, OSInAppMessageInternal *> *redisplayedInAppMessages;
// Tracking for click ids wihtin IAMs so that body, button, and image are only tracked on the dashboard once
// TODO:We should refactor this to be like redisplay logic, save clickId + IAM or by IAM
@property (strong, nonatomic, nonnull) NSMutableSet <NSString *> *clickedClickIds;
// Tracking for impessions so that an IAM is only tracked once and not several times if it is reshown
@property (strong, nonatomic, nonnull) NSMutableSet <NSString *> *impressionedInAppMessages;
// Tracking for impessions so that an IAM is only tracked once and not several times if it is reshown
@property (strong, nonatomic, nonnull) NSMutableSet <NSString *> *viewedPageIDs;
@property (nonatomic) NSMutableArray<NSObject<OSInAppMessageClickListener> *> *clickListeners;
@property (nonatomic) NSMutableArray<NSObject<OSInAppMessageLifecycleListener> *> *lifecycleListeners;
@property (strong, nullable) OSInAppMessageViewController *viewController;
@property (nonatomic, readwrite) NSTimeInterval (^dateGenerator)(void);
@property (nonatomic, nullable) NSObject<OSInAppMessagePrompt> *currentPromptAction;
@property (nonatomic, nullable) NSArray<NSObject<OSInAppMessagePrompt> *> *currentPromptActions;
@property (nonatomic, nullable) OSInAppMessageInternal *currentInAppMessage;
@property (nonatomic) BOOL isAppInactive;
@property (nonatomic) BOOL calledLoadTags;
@end
@implementation OSMessagingController
@dynamic isInAppMessagingPaused;
// Maximum time decided to save IAM with redisplay on cache - current value: six months in seconds
static long OS_IAM_MAX_CACHE_TIME = 6 * 30 * 24 * 60 * 60;
static OSMessagingController *sharedInstance = nil;
static dispatch_once_t once;
+ (OSMessagingController *)sharedInstance {
dispatch_once(&once, ^{
// Make sure only devices with iOS 10 or newer can use IAMs
if ([self doesDeviceSupportIAM]) {
sharedInstance = [OSMessagingController new];
} else {
sharedInstance = [DummyOSMessagingController new];
}
});
return sharedInstance;
}
+ (void)removeInstance {
sharedInstance = nil;
once = 0;
}
+ (void)start {
OSMessagingController *shared = OSMessagingController.sharedInstance;
[OneSignalUserManagerImpl.sharedInstance.pushSubscriptionImpl addObserver:shared];
}
static BOOL _isInAppMessagingPaused = false;
- (BOOL)isInAppMessagingPaused {
return _isInAppMessagingPaused;
}
- (void)setInAppMessagingPaused:(BOOL)pause {
_isInAppMessagingPaused = pause;
// If IAM are not paused, try to evaluate and show IAMs
if (!pause)
[self evaluateMessages];
}
+ (BOOL)doesDeviceSupportIAM {
// We do not support Mac Catalyst as it does not display correctly.
// We could support in the future after we reslove the display issues.
if ([@"Mac" isEqualToString:[OSDeviceUtils getDeviceVariant]])
return false;
// Only support iOS 10 and newer due to Safari 9 WebView issues
return [OSDeviceUtils isIOSVersionGreaterThanOrEqual:@"10.0"];
}
- (instancetype)init {
if (self = [super init]) {
self.dateGenerator = ^ NSTimeInterval {
return [[NSDate date] timeIntervalSince1970];
};
self.messages = [OneSignalUserDefaults.initStandard getSavedCodeableDataForKey:OS_IAM_MESSAGES_ARRAY
defaultValue:[NSArray<OSInAppMessageInternal *> new]];
[self initializeTriggerController];
self.messageDisplayQueue = [NSMutableArray new];
self.clickListeners = [NSMutableArray new];
self.lifecycleListeners = [NSMutableArray new];
let standardUserDefaults = OneSignalUserDefaults.initStandard;
// Get all cached IAM data from NSUserDefaults for shown, impressions, and clicks
self.seenInAppMessages = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_SEEN_SET_KEY defaultValue:nil]];
self.redisplayedInAppMessages = [[NSMutableDictionary alloc] initWithDictionary:[standardUserDefaults getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:[NSMutableDictionary new]]];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"init redisplayedInAppMessages with: %@", [_redisplayedInAppMessages description]]];
self.clickedClickIds = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_CLICKED_SET_KEY defaultValue:nil]];
self.impressionedInAppMessages = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_IMPRESSIONED_SET_KEY defaultValue:nil]];
self.viewedPageIDs = [[NSMutableSet alloc] initWithSet:[standardUserDefaults getSavedSetForKey:OS_IAM_PAGE_IMPRESSIONED_SET_KEY defaultValue:nil]];
self.currentPromptAction = nil;
self.isAppInactive = NO;
// BOOL that controls if in-app messaging is paused or not (false by default)
_isInAppMessagingPaused = false;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleIAMPreview:) name:ONESIGNAL_POST_PREVIEW_IAM object:nil];
}
return self;
}
- (void)initializeTriggerController {
self.triggerController = [OSTriggerController new];
self.triggerController.delegate = self;
NSString *timeSinceLastMessage = [OneSignalUserDefaults.initShared getSavedStringForKey:OS_IAM_TIME_SINCE_LAST_MESSAGE_KEY defaultValue:nil];
[self.triggerController timeSinceLastMessage:[[NSDateFormatter iso8601DateFormatter]
dateFromString:timeSinceLastMessage]];
}
- (void)updateInAppMessagesFromCache {
self.messages = [OneSignalUserDefaults.initStandard getSavedCodeableDataForKey:OS_IAM_MESSAGES_ARRAY defaultValue:[NSArray new]];
[self evaluateMessages];
}
- (void)getInAppMessagesFromServer:(NSString *)subscriptionId {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"getInAppMessagesFromServer"];
if (!subscriptionId) {
[self updateInAppMessagesFromCache];
return;
}
OSRequestGetInAppMessages *request = [OSRequestGetInAppMessages withSubscriptionId:subscriptionId];
[OneSignalCoreImpl.sharedClient executeRequest:request onSuccess:^(NSDictionary *result) {
dispatch_async(dispatch_get_main_queue(), ^{
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"getInAppMessagesFromServer success"];
if (result[@"in_app_messages"]) { // when there are no IAMs, will this still be there?
let messages = [NSMutableArray new];
for (NSDictionary *messageJson in result[@"in_app_messages"]) {
let message = [OSInAppMessageInternal instanceWithJson:messageJson];
if (message) {
[messages addObject:message];
}
}
[self updateInAppMessagesFromServer:messages];
return;
}
// TODO: Check this request and response. If no IAMs returned, should we really get from cache?
// This is the existing implementation but it could mean this user has no IAMs?
// Default is using cached IAMs in the messaging controller
[self updateInAppMessagesFromCache];
});
} onFailure:^(NSError *error) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"getInAppMessagesFromServer failure: %@", error.localizedDescription]];
[self updateInAppMessagesFromCache];
}];
}
- (void)updateInAppMessagesFromServer:(NSArray<OSInAppMessageInternal *> *)newMessages {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"updateInAppMessagesFromServer"];
self.messages = newMessages;
// Cache if messages passed in are not null, this method is called from on_session for
// new messages and cached when foregrounding app
if (self.messages)
[OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_MESSAGES_ARRAY withValue:self.messages];
self.calledLoadTags = NO;
[self resetRedisplayMessagesBySession];
[self evaluateMessages];
[self deleteOldRedisplayedInAppMessages];
}
- (void)resetRedisplayMessagesBySession {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"resetRedisplayMessagesBySession with redisplayedInAppMessages: %@", [_redisplayedInAppMessages description]]];
for (NSString *messageId in _redisplayedInAppMessages) {
[_redisplayedInAppMessages objectForKey:messageId].isDisplayedInSession = false;
}
}
- (void)deleteInactiveMessage:(OSInAppMessageInternal *)message {
let deleteMessage = [NSString stringWithFormat:@"Deleting inactive in-app message from cache: %@", message.messageId];
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:deleteMessage];
NSMutableArray *newMessagesArray = [NSMutableArray arrayWithArray:self.messages];
[newMessagesArray removeObject: message];
self.messages = newMessagesArray;
if (self.messages) {
[OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_MESSAGES_ARRAY withValue:self.messages];
} else {
[OneSignalUserDefaults.initStandard removeValueForKey:OS_IAM_MESSAGES_ARRAY];
}
}
/*
Part of redisplay logic
Remove IAMs that the last display time was six month ago
*/
- (void)deleteOldRedisplayedInAppMessages {
NSMutableSet <NSString *> *messagesIdToRemove = [NSMutableSet new];
let maxCacheTime = self.dateGenerator() - OS_IAM_MAX_CACHE_TIME;
for (NSString *messageId in _redisplayedInAppMessages) {
if ([_redisplayedInAppMessages objectForKey:messageId].displayStats.lastDisplayTime < maxCacheTime) {
[messagesIdToRemove addObject:messageId];
}
}
if ([messagesIdToRemove count] > 0) {
NSMutableDictionary <NSString *, OSInAppMessageInternal *> * newRedisplayDictionary = [_redisplayedInAppMessages mutableCopy];
for (NSString * messageId in messagesIdToRemove) {
[newRedisplayDictionary removeObjectForKey:messageId];
}
[OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:newRedisplayDictionary];
}
}
- (void)addInAppMessageClickListener:(NSObject<OSInAppMessageClickListener> *_Nullable)listener {
[_clickListeners addObject:listener];
}
- (void)removeInAppMessageClickListener:(NSObject<OSInAppMessageClickListener> *_Nullable)listener {
[_clickListeners removeObject:listener];
}
- (void)addInAppMessageLifecycleListener:(NSObject<OSInAppMessageLifecycleListener> *_Nullable)listener {
[_lifecycleListeners addObject:listener];
}
- (void)removeInAppMessageLifecycleListener:(NSObject<OSInAppMessageLifecycleListener> *_Nullable)listener {
[_lifecycleListeners removeObject:listener];
}
- (void)onWillDisplayInAppMessage:(OSInAppMessageInternal *)message {
for (NSObject<OSInAppMessageLifecycleListener> *listener in _lifecycleListeners) {
if ([listener respondsToSelector:@selector(onWillDisplayInAppMessage:)]) {
OSInAppMessageWillDisplayEvent *event = [[OSInAppMessageWillDisplayEvent alloc] initWithInAppMessage:message];
[listener onWillDisplayInAppMessage:event];
}
}
}
- (void)onDidDisplayInAppMessage:(OSInAppMessageInternal *)message {
for (NSObject<OSInAppMessageLifecycleListener> *listener in _lifecycleListeners) {
if ([listener respondsToSelector:@selector(onDidDisplayInAppMessage:)]) {
OSInAppMessageDidDisplayEvent *event = [[OSInAppMessageDidDisplayEvent alloc] initWithInAppMessage:message];
[listener onDidDisplayInAppMessage:event];
}
}
}
- (void)onWillDismissInAppMessage:(OSInAppMessageInternal *)message {
for (NSObject<OSInAppMessageLifecycleListener> *listener in _lifecycleListeners) {
if ([listener respondsToSelector:@selector(onWillDismissInAppMessage:)]) {
OSInAppMessageWillDismissEvent *event = [[OSInAppMessageWillDismissEvent alloc] initWithInAppMessage:message];
[listener onWillDismissInAppMessage:event];
}
}
}
- (void)onDidDismissInAppMessage:(OSInAppMessageInternal *)message {
for (NSObject<OSInAppMessageLifecycleListener> *listener in _lifecycleListeners) {
if ([listener respondsToSelector:@selector(onDidDismissInAppMessage:)]) {
OSInAppMessageDidDismissEvent *event = [[OSInAppMessageDidDismissEvent alloc] initWithInAppMessage:message];
[listener onDidDismissInAppMessage:event];
}
}
}
- (void)handleIAMPreview:(NSNotification *)nsNotification {
OSNotification *notification = [nsNotification.userInfo objectForKey:@"notification"];
OSInAppMessageInternal *message = [OSInAppMessageInternal instancePreviewFromNotification:notification];
[self presentInAppPreviewMessage:message];
}
- (void)presentInAppMessage:(OSInAppMessageInternal *)message {
if (!message.variantId) {
let errorMessage = [NSString stringWithFormat:@"Attempted to display a message with a nil variantId. Current preferred language is %@, supported message variants are %@", OneSignalUserManagerImpl.sharedInstance.language, message.variants];
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:errorMessage];
return;
}
@synchronized (self.messageDisplayQueue) {
// Add the message to the messageDisplayQueue, if it hasn't been added yet
if (![self isMessageInDisplayQueue:message.messageId])
[self.messageDisplayQueue addObject:message];
// Return early if an IAM is already showing
if (self.isInAppMessageShowing) {
return;
}
// Return early if the app is not active
if (![UIApplication applicationIsActive]) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Pause IAMs display due to app inactivity"];
_isAppInactive = YES;
return;
}
[self displayMessage:message];
};
}
- (BOOL)isMessageInDisplayQueue:(NSString *)messageId {
for (OSInAppMessageInternal *message in self.messageDisplayQueue) {
if ([message.messageId isEqualToString:messageId]) {
return true;
}
}
return false;
}
/*
If an IAM is currently showing add the preview right behind it in the messageDisplayQueue and then dismiss the current IAM
Otherwise, Add it to the front of the messageDisplayQueue and call displayMessage
*/
- (void)presentInAppPreviewMessage:(OSInAppMessageInternal *)message {
@synchronized (self.messageDisplayQueue) {
if (self.isInAppMessageShowing) {
// Add preview second in messageDisplayQueue
[self.messageDisplayQueue insertObject:message atIndex:1];
[self.viewController dismissCurrentInAppMessage];
} else {
// Add preview to front of messageDisplayQueue
[self.messageDisplayQueue insertObject:message atIndex:0];
[self displayMessage:message];
}
};
}
- (void)displayMessage:(OSInAppMessageInternal *)message {
// Check if the app disabled IAMs for this device before showing an IAM
if (_isInAppMessagingPaused && !message.isPreview) {
[self cleanUpInAppWindow];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"In app messages will not show while paused"];
return;
}
self.isInAppMessageShowing = true;
[self showMessage:message];
}
- (void)showMessage:(OSInAppMessageInternal *)message {
self.viewController = [[OSInAppMessageViewController alloc] initWithMessage:message delegate:self];
if (message.hasLiquid && !self.calledLoadTags) {
self.viewController.waitForTags = YES;
[self loadTags];
}
dispatch_async(dispatch_get_main_queue(), ^{
[[self.viewController view] setNeedsLayout];
});
}
- (void)sendMessageImpression:(OSInAppMessageInternal *)message {
if ([self shouldSendImpression:message]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self messageViewImpressionRequest:message];
});
}
}
- (void)loadTags {
self.calledLoadTags = YES;
// TODO: should we always pull new tags?
// For now we aren't pulling new tags and we are just using what is already on the user
// I am leaving the logic for waiting for tags to be pulled in case this changes
self.viewController.waitForTags = NO;
}
- (void)messageViewPageImpressionRequest:(OSInAppMessageInternal *)message withPageId:(NSString *)pageId {
if (message.isPreview) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Not sending page impression for preview message. ID: %@",pageId]];
return;
}
if (!pageId) {
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Attempting to send page impression for nil page id"]];
return;
}
NSString *messagePrefixedPageId = [message.messageId stringByAppendingString:pageId];
if ([self.viewedPageIDs containsObject:messagePrefixedPageId]) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Page Impression already sent. id: %@",pageId]];
return;
}
[self.viewedPageIDs addObject:messagePrefixedPageId];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Page Impression Request page id: %@",pageId]];
// Create the request and attach a payload to it
let metricsRequest = [OSRequestInAppMessagePageViewed withAppId:OneSignalConfigManager.getAppId
withPlayerId:OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId
withMessageId:message.messageId
withPageId:pageId
forVariantId:message.variantId];
[OneSignalCoreImpl.sharedClient executeRequest:metricsRequest
onSuccess:^(NSDictionary *result) {
NSString *successMessage = [NSString stringWithFormat:@"In App Message with message id: %@ and page id: %@, successful POST page impression update with result: %@", message.messageId, pageId, result];
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:successMessage];
// If the post was successful, save the updated viewedPageIds set
[OneSignalUserDefaults.initStandard saveSetForKey:OS_IAM_PAGE_IMPRESSIONED_SET_KEY withValue:self.viewedPageIDs];
}
onFailure:^(NSError *error) {
NSString *errorMessage = [NSString stringWithFormat:@"In App Message with message id: %@ and page id: %@, failed POST page impression update with error: %@", message.messageId, pageId, error];
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:errorMessage];
if (message) {
[self.viewedPageIDs removeObject:messagePrefixedPageId];
}
}];
}
- (BOOL)shouldSendImpression:(OSInAppMessageInternal *)message {
return !(message.isPreview || [self.impressionedInAppMessages containsObject:message.messageId]);
}
/*
Make an impression POST to track that the IAM has been
Request should only be made for IAMs that are not previews and have not been impressioned yet
*/
- (void)messageViewImpressionRequest:(OSInAppMessageInternal *)message {
// Make sure no tracking is performed for previewed IAMs
// If the messageId exists in cached impressionedInAppMessages return early so the impression is not tracked again
if (![self shouldSendImpression:message])
return;
// Add messageId to impressionedInAppMessages
[self.impressionedInAppMessages addObject:message.messageId];
// Create the request and attach a payload to it
let metricsRequest = [OSRequestInAppMessageViewed withAppId:OneSignalConfigManager.getAppId
withPlayerId:OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId
withMessageId:message.messageId
forVariantId:message.variantId];
[OneSignalCoreImpl.sharedClient executeRequest:metricsRequest
onSuccess:^(NSDictionary *result) {
NSString *successMessage = [NSString stringWithFormat:@"In App Message with id: %@, successful POST impression update with result: %@", message.messageId, result];
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:successMessage];
// If the post was successful, save the updated impressionedInAppMessages set
[OneSignalUserDefaults.initStandard saveSetForKey:OS_IAM_IMPRESSIONED_SET_KEY withValue:self.impressionedInAppMessages];
}
onFailure:^(NSError *error) {
NSString *errorMessage = [NSString stringWithFormat:@"In App Message with id: %@, failed POST impression update with error: %@", message.messageId, error];
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:errorMessage];
// If the post failed, remove the messageId from the impressionedInAppMessages set
[self.impressionedInAppMessages removeObject:message.messageId];
}];
}
/*
Checks to see if any messages should be shown now
*/
- (void)evaluateMessages {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Evaluating in app messages"];
for (OSInAppMessageInternal *message in self.messages) {
if ([self.triggerController messageMatchesTriggers:message]) {
// Make changes to IAM if redisplay available
[self setDataForRedisplay:message];
// Should we show the in app message
if ([self shouldShowInAppMessage:message]) {
[self presentInAppMessage:message];
}
}
}
}
/*
Part of redisplay logic
In order to redisplay an IAM, the following conditions must be satisfied:
1. IAM has redisplay property
2. Time delay between redisplay satisfied
3. Has more redisplays
4. An IAM trigger was satisfied
For redisplay, the message need to be removed from the arrays that track the display/impression
For click counting, every message has it click id array
*/
- (void)setDataForRedisplay:(OSInAppMessageInternal *)message {
if (!message.displayStats.isRedisplayEnabled) {
return;
}
BOOL messageDismissed = [_seenInAppMessages containsObject:message.messageId];
let redisplayMessageSavedData = [_redisplayedInAppMessages objectForKey:message.messageId];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Redisplay dismissed: %@ and data: %@", messageDismissed ? @"YES" : @"NO", redisplayMessageSavedData.jsonRepresentationInternal.description]];
if (messageDismissed && redisplayMessageSavedData) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Redisplay IAM: %@", message.jsonRepresentationInternal.description]];
message.displayStats.displayQuantity = redisplayMessageSavedData.displayStats.displayQuantity;
message.displayStats.lastDisplayTime = redisplayMessageSavedData.displayStats.lastDisplayTime;
// Message that don't have triggers should display only once per session
BOOL triggerHasChanged = [self hasMessageTriggerChanged:message];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"setDataForRedisplay with message: %@ \ntriggerHasChanged: %@ \nno triggers: %@ \ndisplayed in session saved: %@", message, message.isTriggerChanged ? @"YES" : @"NO", [message.triggers count] == 0 ? @"YES" : @"NO", redisplayMessageSavedData.isDisplayedInSession ? @"YES" : @"NO"]];
// Check if conditions are correct for redisplay
if (triggerHasChanged &&
[message.displayStats isDelayTimeSatisfied:self.dateGenerator()] &&
[message.displayStats shouldDisplayAgain]) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"setDataForRedisplay clear arrays"];
[self.seenInAppMessages removeObject:message.messageId];
[self.impressionedInAppMessages removeObject:message.messageId];
[self.viewedPageIDs removeAllObjects];
[OneSignalUserDefaults.initStandard saveSetForKey:OS_IAM_PAGE_IMPRESSIONED_SET_KEY withValue:self.viewedPageIDs];
[message clearClickIds];
return;
}
}
}
- (BOOL)hasMessageTriggerChanged:(OSInAppMessageInternal *)message {
// Message that only have dynamic trigger should display only once per session
BOOL messageHasOnlyDynamicTrigger = [self.triggerController messageHasOnlyDynamicTriggers:message];
if (messageHasOnlyDynamicTrigger)
return !message.isDisplayedInSession;
// Message that don't have triggers should display only once per session
BOOL shouldMessageDisplayInSession = !message.isDisplayedInSession && [message.triggers count] == 0;
return message.isTriggerChanged || shouldMessageDisplayInSession;
}
/*
Method to check whether or not to show an IAM
Checks if the IAM matches any triggers or if it exists in cached seenInAppMessages set
*/
- (BOOL)shouldShowInAppMessage:(OSInAppMessageInternal *)message {
return ![self.seenInAppMessages containsObject:message.messageId] &&
[self.triggerController messageMatchesTriggers:message] &&
![message isFinished] &&
OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId != nil;
return true;
}
- (void)handleMessageActionWithURL:(OSInAppMessageClickResult *)action {
switch (action.urlTarget) {
case OSInAppMessageActionUrlTypeSafari:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:action.url] options:@{} completionHandler:^(BOOL success) {}];
break;
case OSInAppMessageActionUrlTypeWebview:
[OneSignalWebViewManager displayWebView:[NSURL URLWithString:action.url]];
break;
case OSInAppMessageActionUrlTypeReplaceContent:
// This case is handled by the in-app message view controller.
break;
}
}
/*
* Part of redisplay logic
*
* Make all messages with redisplay available if:
* - Already displayed
* - At least one Trigger has changed
*/
- (void)evaluateRedisplayedInAppMessages:(NSArray<NSString *> *)newTriggersKeys {
for (OSInAppMessageInternal *message in _messages) {
if ([_redisplayedInAppMessages objectForKey:message.messageId] &&
[self.triggerController hasSharedTriggers:message newTriggersKeys:newTriggersKeys]) {
message.isTriggerChanged = true;
}
}
}
#pragma mark Trigger Methods
- (void)addTriggers:(NSDictionary<NSString *, id> *)triggers {
[self evaluateRedisplayedInAppMessages:triggers.allKeys];
[self.triggerController addTriggers:triggers];
}
- (void)removeTriggersForKeys:(NSArray<NSString *> *)keys {
[self evaluateRedisplayedInAppMessages:keys];
[self.triggerController removeTriggersForKeys:keys];
}
- (void)clearTriggers {
NSDictionary<NSString *, id> *allTriggers = [self getTriggers];
[self removeTriggersForKeys:allTriggers.allKeys];
}
- (NSDictionary<NSString *, id> *)getTriggers {
return self.triggerController.getTriggers;
}
- (id)getTriggerValueForKey:(NSString *)key {
return [self.triggerController getTriggerValueForKey:key];
}
#pragma mark OSInAppMessageViewControllerDelegate Methods
- (void)messageViewControllerDidDisplay:(OSInAppMessageInternal *)message {
[self onDidDisplayInAppMessage:message];
}
- (void)messageViewControllerWillDismiss:(OSInAppMessageInternal *)message {
[self onWillDismissInAppMessage:message];
}
- (void)messageViewControllerWasDismissed:(OSInAppMessageInternal *)message displayed:(BOOL)displayed {
@synchronized (self.messageDisplayQueue) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Dismissing IAM and preparing to show next IAM"];
// Remove DIRECT influence due to ClickHandler of ClickAction outcomes
[[OSSessionManager sharedSessionManager] onDirectInfluenceFromIAMClickFinished];
// Add current dismissed messageId to seenInAppMessages set and save it to NSUserDefaults
if (self.isInAppMessageShowing) {
if (displayed) {
[self onDidDismissInAppMessage:message];
}
OSInAppMessageInternal *showingIAM = self.messageDisplayQueue.firstObject;
[self.seenInAppMessages addObject:showingIAM.messageId];
[OneSignalUserDefaults.initStandard saveSetForKey:OS_IAM_SEEN_SET_KEY withValue:self.seenInAppMessages];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Dismissing IAM save seenInAppMessages: %@", _seenInAppMessages]];
// Remove dismissed IAM from messageDisplayQueue
[self.messageDisplayQueue removeObjectAtIndex:0];
[self persistInAppMessageForRedisplay:showingIAM];
}
// Reset the IAM viewController to prepare for next IAM if one exists
self.viewController = nil;
// Reset time since last IAM
[self setAndPersistTimeSinceLastMessage];
if (!_currentPromptAction) {
[self evaluateMessageDisplayQueue];
} else { //do nothing prompt is handling the re-showing
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Stop evaluateMessageDisplayQueue because prompt is currently displayed"];
}
}
}
- (void)cleanUpInAppWindow {
self.viewController = nil;
if (self.window) {
/*
Hide the top level IAM window
After the IAM window is hidden, iOS will automatically promote the main window
This also re-shows the keyboard automatically if it had focus in a text input
*/
self.window.hidden = true;
self.window = nil;
}
}
- (void)setAndPersistTimeSinceLastMessage {
NSDate *timeSinceLastMessage = [NSDate new];
[self.triggerController timeSinceLastMessage:timeSinceLastMessage];
NSString *stringTimeSinceLastMessage = [[NSDateFormatter iso8601DateFormatter]
stringFromDate:timeSinceLastMessage];
[OneSignalUserDefaults.initShared saveStringForKey:OS_IAM_TIME_SINCE_LAST_MESSAGE_KEY
withValue:stringTimeSinceLastMessage];
}
- (void)evaluateMessageDisplayQueue {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"Evaluating message display queue"];
// No IAMs are showing currently
self.isInAppMessageShowing = false;
if (self.messageDisplayQueue.count > 0) {
// Show next IAM in queue
[self displayMessage:self.messageDisplayQueue.firstObject];
return;
} else {
[self cleanUpInAppWindow];
// Evaulate any IAMs (could be new IAM or added trigger conditions)
[self evaluateMessages];
}
}
- (void)persistInAppMessageForRedisplay:(OSInAppMessageInternal *)message {
// If the IAM doesn't have the re display prop or is a preview IAM there is no need to save it
if (![message.displayStats isRedisplayEnabled] || message.isPreview) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"not persisting %@",message.displayStats]];
return;
}
let displayTimeSeconds = self.dateGenerator();
message.displayStats.lastDisplayTime = displayTimeSeconds;
[message.displayStats incrementDisplayQuantity];
message.isTriggerChanged = false;
message.isDisplayedInSession = true;
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"redisplayedInAppMessages: %@", [_redisplayedInAppMessages description]]];
// Update the data to enable future re displays
// Avoid calling the userdefault data again
[_redisplayedInAppMessages setObject:message forKey:message.messageId];
[OneSignalUserDefaults.initStandard saveCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY withValue:_redisplayedInAppMessages];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"persistInAppMessageForRedisplay: %@ \nredisplayedInAppMessages: %@", [message description], _redisplayedInAppMessages]];
let standardUserDefaults = OneSignalUserDefaults.initStandard;
let redisplayedInAppMessages = [[NSMutableDictionary alloc] initWithDictionary:[standardUserDefaults getSavedCodeableDataForKey:OS_IAM_REDISPLAY_DICTIONARY defaultValue:[NSMutableDictionary new]]];
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"persistInAppMessageForRedisplay saved redisplayedInAppMessages: %@", [redisplayedInAppMessages description]]];
}
- (void)handlePromptActions:(NSArray<NSObject<OSInAppMessagePrompt> *> *)promptActions withMessage:(OSInAppMessageInternal *)inAppMessage {
_currentPromptAction = nil;
for (NSObject<OSInAppMessagePrompt> *promptAction in promptActions) {
// Don't show prompt twice
if (!promptAction.hasPrompted) {
_currentPromptAction = promptAction;
break;
}
}
if (_currentPromptAction) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"IAM prompt to handle: %@", [_currentPromptAction description]]];
_currentPromptAction.hasPrompted = YES;
[_currentPromptAction handlePrompt:^(PromptActionResult result) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"IAM prompt to handle finished accepted: %u", result]];
if (inAppMessage.isPreview && result == LOCATION_PERMISSIONS_MISSING_INFO_PLIST) {
[self showAlertDialogMessage:inAppMessage promptActions:promptActions];
} else {
[self handlePromptActions:promptActions withMessage:inAppMessage];
}
}];
} else if (!_viewController) { // IAM dismissed by action
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:@"IAM with prompt dismissed from actionTaken"];
_currentInAppMessage = nil;
_currentPromptActions = nil;
[self evaluateMessageDisplayQueue];
}
}
- (void)showAlertDialogMessage:(OSInAppMessageInternal *)inAppMessage
promptActions:(NSArray<NSObject<OSInAppMessagePrompt> *> *)promptActions {
_currentInAppMessage = inAppMessage;
_currentPromptActions = promptActions;
let message = NSLocalizedString(@"Looks like this app doesn't have location services configured. Please see OneSignal docs for more information.", @"An alert message indicating that the application is not configured to use have location services.");
let title = NSLocalizedString(@"Location Not Available", @"An alert title indicating that the location service is unavailable.");
let okAction = NSLocalizedString(@"OK", @"Allows the user to acknowledge and dismiss the alert");
[[OSDialogInstanceManager sharedInstance] presentDialogWithTitle:title withMessage:message withActions:nil cancelTitle:okAction withActionCompletion:^(int tappedActionIndex) {
//completion is called on the main thread
[self handlePromptActions:_currentPromptActions withMessage:_currentInAppMessage];
}];
}
- (void)messageViewDidSelectAction:(OSInAppMessageInternal *)message withAction:(OSInAppMessageClickResult *)action {
// Assign firstClick BOOL based on message being clicked previously or not
action.firstClick = [message takeActionAsUnique];
if (action.url)
[self handleMessageActionWithURL:action];
if (action.promptActions && action.promptActions.count > 0)
[self handlePromptActions:action.promptActions withMessage:message];
if (_clickListeners.count > 0) {
// Any outcome sent on the listener's callback should count as DIRECT from this IAM
[[OSSessionManager sharedSessionManager] onDirectInfluenceFromIAMClick:message.messageId];
}
for (NSObject<OSInAppMessageClickListener> *listener in _clickListeners) {
if ([listener respondsToSelector:@selector(onClickInAppMessage:)]) {
OSInAppMessageClickEvent *event = [[OSInAppMessageClickEvent alloc] initWithInAppMessage:message clickResult:action];
[listener onClickInAppMessage:event];
}
}
if (message.isPreview) {
[self processPreviewInAppMessage:message withAction:action];
return;
}
// The following features are for non preview IAM
// Make sure no click, outcome, tag tracking is performed for IAM previews
[self sendClickRESTCall:message withAction:action];
[self sendTagCallWithAction:action];
[self sendOutcomes:action.outcomes forMessageId:message.messageId];
}
- (void)messageViewDidDisplayPage:(OSInAppMessageInternal *)message withPageId:(NSString *)pageId {
dispatch_async(dispatch_get_main_queue(), ^{
[self messageViewPageImpressionRequest:message withPageId:pageId];
});
}
- (void)messageIsNotActive:(OSInAppMessageInternal *)message {
[self deleteInactiveMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[self cleanUpInAppWindow];
});
}
- (void)messageWillDisplay:(nonnull OSInAppMessageInternal *)message {
[self onWillDisplayInAppMessage:message];
}
/*
* Show the developer what will happen with a non IAM preview
*/
- (void)processPreviewInAppMessage:(OSInAppMessageInternal *)message withAction:(OSInAppMessageClickResult *)action {
if (action.tags)
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Tags detected inside of the action click payload, ignoring because action came from IAM preview\nTags: %@", action.tags.jsonRepresentation]];
if (action.outcomes.count > 0) {
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"Outcomes detected inside of the action click payload, ignoring because action came from IAM preview: %@", [action.outcomes description]]];
}
}
/*
* Checks if a click being available:
* 1. Redisplay is enabled and click is available within message
* 2. Click clickId should not be inside of the clickedClickIds set
*/
- (BOOL)isClickAvailable:(OSInAppMessageInternal *)message withClickId:(NSString *)clickId {
// If IAM has redisplay the clickId may be available
return ([message.displayStats isRedisplayEnabled] && [message isClickAvailable:clickId]) || ![_clickedClickIds containsObject:clickId];
}
- (void)sendClickRESTCall:(OSInAppMessageInternal *)message withAction:(OSInAppMessageClickResult *)action {
let clickId = action.clickId;
// If the IAM clickId exists within the cached clickedClickIds return early so the click is not tracked
// unless that click is from an IAM with redisplay
// Handles body, button, or image clicks
if (![self isClickAvailable:message withClickId:clickId])
return;
if (!clickId) {
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:@"sendClickRESTCall:withAction: call could not be made because the click action does not have an id."];
return;
}
// Add clickId to clickedClickIds
[self.clickedClickIds addObject:clickId];
// Track clickId per IAM
[message addClickId:clickId];
let metricsRequest = [OSRequestInAppMessageClicked withAppId:OneSignalConfigManager.getAppId
withPlayerId:OneSignalUserManagerImpl.sharedInstance.pushSubscriptionId
withMessageId:message.messageId
forVariantId:message.variantId
withAction:action];
[OneSignalCoreImpl.sharedClient executeRequest:metricsRequest
onSuccess:^(NSDictionary *result) {
NSString *successMessage = [NSString stringWithFormat:@"In App Message with id: %@, successful POST click update for click id: %@, with result: %@", message.messageId, action.clickId, result];
[OneSignalLog onesignalLog:ONE_S_LL_DEBUG message:successMessage];
// Save the updated clickedClickIds since click was tracked successfully
[OneSignalUserDefaults.initStandard saveSetForKey:OS_IAM_CLICKED_SET_KEY withValue:self.clickedClickIds];
}
onFailure:^(NSError *error) {
NSString *errorMessage = [NSString stringWithFormat:@"In App Message with id: %@, failed POST click update for click id: %@, with error: %@", message.messageId, action.clickId, error];
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:errorMessage];
// Remove clickId from local clickedClickIds since click was not tracked
[self.clickedClickIds removeObject:action.clickId];
}];
}
- (void)sendTagCallWithAction:(OSInAppMessageClickResult *)action {
if (action.tags) {
OSInAppMessageTag *tag = action.tags;
if (tag.tagsToAdd)
[OneSignalUserManagerImpl.sharedInstance addTags:tag.tagsToAdd];
if (tag.tagsToRemove)
[OneSignalUserManagerImpl.sharedInstance removeTags:tag.tagsToRemove];
}
}
- (void)sendOutcomes:(NSArray<OSInAppMessageOutcome *>*)outcomes forMessageId:(NSString *) messageId {
if (outcomes.count == 0)
return;
[[OSSessionManager sharedSessionManager] onDirectInfluenceFromIAMClick:messageId];
[self sendClickActionOutcomes:outcomes];
}
- (void)sendClickActionOutcomes:(NSArray<OSInAppMessageOutcome *> *)outcomes {
if (![OSOutcomes sharedController]) {
[OneSignalLog onesignalLog:ONE_S_LL_ERROR message:@"Make sure OneSignal init is called first"];
return;
}
[OSOutcomes.sharedController sendClickActionOutcomes:outcomes appId:OneSignalConfigManager.getAppId deviceType:[NSNumber numberWithInt:DEVICE_TYPE_PUSH]];
}
/*