Skip to content
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
12 changes: 12 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@
CAABF34B205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CAABF34A205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m */; };
CAABF34C205B157B0042F8E5 /* OneSignalExtensionBadgeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CAABF34A205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m */; };
CAABF34D205B157B0042F8E5 /* OneSignalExtensionBadgeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CAABF34A205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m */; };
CAAEA68721ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAAEA68521ED68A30049CF15 /* OneSignalNotificationCategoryController.m */; };
CAAEA68821ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAAEA68521ED68A30049CF15 /* OneSignalNotificationCategoryController.m */; };
CAAEA68921ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAAEA68521ED68A30049CF15 /* OneSignalNotificationCategoryController.m */; };
CAAEA68A21ED68A40049CF15 /* OneSignalNotificationCategoryController.h in Headers */ = {isa = PBXBuildFile; fileRef = CAAEA68621ED68A40049CF15 /* OneSignalNotificationCategoryController.h */; };
CAB4112920852E48005A70D1 /* DelayedInitializationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CAB4112820852E48005A70D1 /* DelayedInitializationParameters.m */; };
CAB4112A20852E4C005A70D1 /* DelayedInitializationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CAB4112820852E48005A70D1 /* DelayedInitializationParameters.m */; };
CAB4112B20852E4C005A70D1 /* DelayedInitializationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = CAB4112820852E48005A70D1 /* DelayedInitializationParameters.m */; };
Expand Down Expand Up @@ -331,6 +335,8 @@
CAA4ED0020646762005BD59B /* BadgeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BadgeTests.m; sourceTree = "<group>"; };
CAABF349205B15780042F8E5 /* OneSignalExtensionBadgeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalExtensionBadgeHandler.h; sourceTree = "<group>"; };
CAABF34A205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalExtensionBadgeHandler.m; sourceTree = "<group>"; };
CAAEA68521ED68A30049CF15 /* OneSignalNotificationCategoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OneSignalNotificationCategoryController.m; sourceTree = "<group>"; };
CAAEA68621ED68A40049CF15 /* OneSignalNotificationCategoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OneSignalNotificationCategoryController.h; sourceTree = "<group>"; };
CAB4112720852E48005A70D1 /* DelayedInitializationParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DelayedInitializationParameters.h; sourceTree = "<group>"; };
CAB4112820852E48005A70D1 /* DelayedInitializationParameters.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DelayedInitializationParameters.m; sourceTree = "<group>"; };
CAB411AC208931EE005A70D1 /* DummyNotificationCenterDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DummyNotificationCenterDelegate.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -514,6 +520,8 @@
454F94F11FAD218000D74CCF /* OneSignalNotificationServiceExtensionHandler.m */,
CAABF349205B15780042F8E5 /* OneSignalExtensionBadgeHandler.h */,
CAABF34A205B15780042F8E5 /* OneSignalExtensionBadgeHandler.m */,
CAAEA68621ED68A40049CF15 /* OneSignalNotificationCategoryController.h */,
CAAEA68521ED68A30049CF15 /* OneSignalNotificationCategoryController.m */,
);
path = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -627,6 +635,7 @@
9124121D1E73342200E41FD7 /* OneSignalJailbreakDetection.h in Headers */,
9129C6B71E89E59B009CB6A0 /* OSPermission.h in Headers */,
912412151E73342200E41FD7 /* OneSignalHelper.h in Headers */,
CAAEA68A21ED68A40049CF15 /* OneSignalNotificationCategoryController.h in Headers */,
91F58D7F1E7C7F5F0017D24D /* OneSignalNotificationSettingsIOS10.h in Headers */,
912412391E73342200E41FD7 /* OneSignalWebView.h in Headers */,
91C7725E1E7CCE1000D612D0 /* OneSignalInternal.h in Headers */,
Expand Down Expand Up @@ -796,6 +805,7 @@
9124120E1E73342200E41FD7 /* OneSignal.m in Sources */,
CA08FC731FE99AFD004C445F /* OneSignalClient.m in Sources */,
91F58D831E7C80DA0017D24D /* OneSignalNotificationSettingsIOS8.m in Sources */,
CAAEA68721ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */,
9124121E1E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */,
CA08FC791FE99B13004C445F /* OneSignalRequest.m in Sources */,
912412471E73369600E41FD7 /* OneSignalHelper.m in Sources */,
Expand Down Expand Up @@ -837,6 +847,7 @@
9124120F1E73342200E41FD7 /* OneSignal.m in Sources */,
CA08FC741FE99AFF004C445F /* OneSignalClient.m in Sources */,
91F58D861E7C88250017D24D /* OneSignalNotificationSettingsIOS8.m in Sources */,
CAAEA68821ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */,
9124121F1E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */,
CA08FC7A1FE99B13004C445F /* OneSignalRequest.m in Sources */,
912412481E73369700E41FD7 /* OneSignalHelper.m in Sources */,
Expand Down Expand Up @@ -879,6 +890,7 @@
91F58D8B1E7C9A240017D24D /* OneSignalNotificationSettingsIOS7.m in Sources */,
91F60F7D1E80E4E400706E60 /* UncaughtExceptionHandler.m in Sources */,
912412201E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */,
CAAEA68921ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */,
CA85C15320604AEA003AB529 /* RequestTests.m in Sources */,
CAE2E5A8215D80010036FD32 /* OneSignalTrackFirebaseAnalytics.m in Sources */,
912412381E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions iOS_SDK/OneSignalSDK/Source/OneSignalCommonDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE} HTTPMethod;
// before registering the user anyways
#define APNS_TIMEOUT 25.0

// The SDK saves a list of category ID's allowing multiple notifications
// to have their own unique buttons/etc.
#define SHARED_CATEGORY_LIST @"com.onesignal.shared_registered_categories"

#ifndef OS_TEST
// OneSignal API Client Defines
#define REATTEMPT_DELAY 30.0
Expand All @@ -123,6 +127,9 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE} HTTPMethod;

// Send tags batch delay
#define SEND_TAGS_DELAY 5.0

// the max number of UNNotificationCategory ID's the SDK will register
#define MAX_CATEGORIES_SIZE 128
#else
// Test defines for API Client
#define REATTEMPT_DELAY 0.004
Expand All @@ -132,6 +139,9 @@ typedef enum {GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE} HTTPMethod;

// Send tags batch delay
#define SEND_TAGS_DELAY 0.005

// the max number of UNNotificationCategory ID's the SDK will register
#define MAX_CATEGORIES_SIZE 5
#endif

// A max timeout for a request, which might include multiple reattempts
Expand Down
24 changes: 7 additions & 17 deletions iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#import "NSURL+OneSignal.h"
#import "OneSignalCommonDefines.h"
#import "OneSignalDialogController.h"
#import "OneSignalNotificationCategoryController.h"

#define NOTIFICATION_TYPE_ALL 7
#pragma clang diagnostic push
Expand Down Expand Up @@ -596,17 +597,19 @@ + (void)addActionButtons:(OSNotificationPayload*)payload
finalActionArray = actionArray;

// Get a full list of categories so we don't replace any exisiting ones.
var allCategories = [self existingCategories];
var allCategories = OneSignalNotificationCategoryController.sharedInstance.existingCategories;

let category = [UNNotificationCategory categoryWithIdentifier:@"__dynamic__"
let newCategoryIdentifier = [OneSignalNotificationCategoryController.sharedInstance registerNotificationCategoryForNotificationId:payload.notificationID];

let category = [UNNotificationCategory categoryWithIdentifier:newCategoryIdentifier
actions:finalActionArray
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];

if (allCategories) {
let newCategorySet = [NSMutableSet new];
for(UNNotificationCategory *existingCategory in allCategories) {
if (![existingCategory.identifier isEqualToString:@"__dynamic__"])
if (![existingCategory.identifier isEqualToString:newCategoryIdentifier])
[newCategorySet addObject:existingCategory];
}

Expand All @@ -618,20 +621,7 @@ + (void)addActionButtons:(OSNotificationPayload*)payload

[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:allCategories];

content.categoryIdentifier = @"__dynamic__";
}

+ (NSMutableSet<UNNotificationCategory*>*)existingCategories {
__block NSMutableSet* allCategories;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
let notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
[notificationCenter getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> *categories) {
allCategories = [categories mutableCopy];
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return allCategories;
content.categoryIdentifier = newCategoryIdentifier;
}

+ (void)addAttachments:(OSNotificationPayload*)payload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* 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 <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>

NS_ASSUME_NONNULL_BEGIN


/**
This class maintains a saved list of UNNotificationCategory ID
strings. Allows the SDK to store unique UNNotificationCategory
objects for each notification.

The SDK automatically prunes notification categories once more
than MAX_CATEGORIES_SIZE categories have been registered.
*/

@interface OneSignalNotificationCategoryController : NSObject

+ (OneSignalNotificationCategoryController *)sharedInstance;

- (NSString *)registerNotificationCategoryForNotificationId:(NSString *)notificationId;

- (NSMutableSet<UNNotificationCategory*>*)existingCategories;

@end

NS_ASSUME_NONNULL_END
120 changes: 120 additions & 0 deletions iOS_SDK/OneSignalSDK/Source/OneSignalNotificationCategoryController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* 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 "OneSignalNotificationCategoryController.h"
#import "OneSignalExtensionBadgeHandler.h"
#import "OneSignalHelper.h"
#import "OneSignalCommonDefines.h"

#define CATEGORY_FORMAT_STRING(notificationId) [NSString stringWithFormat:@"__onesignal__dynamic__%@", notificationId]

@implementation OneSignalNotificationCategoryController

+ (OneSignalNotificationCategoryController *)sharedInstance {
static OneSignalNotificationCategoryController *sharedInstance = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [OneSignalNotificationCategoryController new];
});
return sharedInstance;
}

// appends the new category ID to the current saved array of category ID's
// The array is then inherently sorted in ascending order (the ID at index 0 is the oldest)
// we want to run this on the main thread so that the extension service doesn't stop before it finishes
// To prevent the SDK from registering too many categories as time goes by, we will prune the categories
// when more than MAX_CATEGORIES_SIZE have been registered
- (void)saveCategoryId:(NSString *)categoryId {
let defaults = [[NSUserDefaults alloc] initWithSuiteName:OneSignalExtensionBadgeHandler.appGroupName];

NSMutableArray<NSString *> *mutableExisting = [self.existingRegisteredCategoryIds mutableCopy];

[mutableExisting addObject:categoryId];

// prune array if > max size
if (mutableExisting.count > MAX_CATEGORIES_SIZE) {

// removes these categories from UNUserNotificationCenter
[self pruneCategories:mutableExisting];

[mutableExisting removeObjectsInRange:NSMakeRange(0, mutableExisting.count - MAX_CATEGORIES_SIZE)];
}


[defaults setObject:mutableExisting forKey:SHARED_CATEGORY_LIST];

[defaults synchronize];
}

- (NSArray<NSString *> *)existingRegisteredCategoryIds {
let defaults = [[NSUserDefaults alloc] initWithSuiteName:OneSignalExtensionBadgeHandler.appGroupName];

NSArray<NSString *> *existing = [defaults arrayForKey:SHARED_CATEGORY_LIST] ?: [NSArray new];

return existing;
}

- (void)pruneCategories:(NSMutableArray <NSString *> *)currentCategories {
NSMutableSet<NSString *> *categoriesToRemove = [NSMutableSet new];

for (int i = (int)currentCategories.count - MAX_CATEGORIES_SIZE; i >= 0; i--)
[categoriesToRemove addObject:currentCategories[i]];

let existingCategories = self.existingCategories;

NSMutableSet<UNNotificationCategory *> *newCategories = [NSMutableSet new];

for (UNNotificationCategory *category in existingCategories)
if (![categoriesToRemove containsObject:category.identifier])
[newCategories addObject:category];

[UNUserNotificationCenter.currentNotificationCenter setNotificationCategories:newCategories];
}

- (NSString *)registerNotificationCategoryForNotificationId:(NSString *)notificationId {
// if the notificationID is null/empty, just generate a random new UUID
let categoryId = CATEGORY_FORMAT_STRING(notificationId ?: NSUUID.UUID.UUIDString);

[self saveCategoryId:categoryId];

return categoryId;
}

- (NSMutableSet<UNNotificationCategory*>*)existingCategories {
__block NSMutableSet* allCategories;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
let notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
[notificationCenter getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> *categories) {
allCategories = [categories mutableCopy];
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return allCategories;
}

@end
7 changes: 7 additions & 0 deletions iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "OneSignal.h"
#import "OneSignalNotificationCategoryController.h"

#define TEST_EXTERNAL_USER_ID @"i_am_a_test_external_user_id"

Expand All @@ -53,6 +54,12 @@ NSString * serverUrlWithPath(NSString *path);
+ (void)setDelayIntervals:(NSTimeInterval)apnsMaxWait withRegistrationDelay:(NSTimeInterval)registrationDelay;
@end

// Expose methods on OneSignalNotificationCategoryController
@interface OneSignalNotificationCategoryController ()
- (void)pruneCategories:(NSMutableArray <NSString *> *)currentCategories;
- (NSArray<NSString *> *)existingRegisteredCategoryIds;
@end

// START - Start Observers

@interface OSPermissionStateTestObserver : NSObject<OSPermissionObserver> {
Expand Down
Loading