Skip to content

Commit c692754

Browse files
committed
Adding subClass swizzling check to UNUserNotificationCenter
1 parent 3a4ae25 commit c692754

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

iOS_SDK/OneSignalSDK/Source/UNUserNotificationCenter+OneSignal.m

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#import "UIApplicationDelegate+OneSignal.h"
3838
#import "OneSignalCommonDefines.h"
3939
#import "SwizzlingForwarder.h"
40+
#import <objc/runtime.h>
4041
#pragma clang diagnostic push
4142
#pragma clang diagnostic ignored "-Wundeclared-selector"
4243

@@ -192,7 +193,7 @@ - (void) setOneSignalUNDelegate:(id)delegate {
192193

193194
Class delegateClass = [delegate class];
194195

195-
if (delegate == nil || [swizzledClasses containsObject:delegateClass]) {
196+
if (delegate == nil || [OneSignalUNUserNotificationCenter swizzledClassInHeirarchy:delegateClass]) {
196197
[self setOneSignalUNDelegate:delegate];
197198
return;
198199
}
@@ -206,6 +207,22 @@ - (void) setOneSignalUNDelegate:(id)delegate {
206207
[self setOneSignalUNDelegate:delegate];
207208
}
208209

210+
+ (BOOL)swizzledClassInHeirarchy:(Class)delegateClass {
211+
if ([swizzledClasses containsObject:delegateClass]) {
212+
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"OneSignal already swizzled %@", NSStringFromClass(delegateClass)]];
213+
return true;
214+
}
215+
Class superClass = class_getSuperclass(delegateClass);
216+
while(superClass) {
217+
if ([swizzledClasses containsObject:superClass]) {
218+
[OneSignalLog onesignalLog:ONE_S_LL_VERBOSE message:[NSString stringWithFormat:@"OneSignal already swizzled %@ in super class: %@", NSStringFromClass(delegateClass), NSStringFromClass(superClass)]];
219+
return true;
220+
}
221+
superClass = class_getSuperclass(superClass);
222+
}
223+
return false;
224+
}
225+
209226
+ (void)swizzleSelectorsOnDelegate:(id)delegate {
210227
Class delegateUNClass = [delegate class];
211228
injectSelector(

iOS_SDK/OneSignalSDK/UnitTests/OneSignalUNUserNotificationCenterSwizzlingTest.m

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ @interface UNUserNotificationCenterDelegateForInfiniteLoopWithAnotherSwizzlerTes
130130
@end
131131
@implementation UNUserNotificationCenterDelegateForInfiniteLoopWithAnotherSwizzlerTest
132132
@end
133-
@interface OtherUNNotificationLibraryASwizzler : NSObject
133+
@interface OtherUNNotificationLibraryASwizzler : UIResponder<UNUserNotificationCenterDelegate>
134134
+(void)swizzleUNUserNotificationCenterDelegate;
135135
+(BOOL)selectorCalled;
136136
@end
@@ -157,7 +157,33 @@ -(void)userNotificationCenterLibraryA:(UNUserNotificationCenter *)center willPre
157157
[self userNotificationCenterLibraryA:center willPresentNotification:notification withCompletionHandler:completionHandler];
158158
}
159159
@end
160+
@interface OtherUNNotificationLibraryBSubClassSwizzler : OtherUNNotificationLibraryASwizzler
161+
+(void)swizzleUNUserNotificationCenterDelegate;
162+
+(BOOL)selectorCalled;
163+
@end
164+
@implementation OtherUNNotificationLibraryBSubClassSwizzler
160165

166+
+(BOOL)selectorCalled {
167+
return selectorCalled;
168+
}
169+
170+
+(void)swizzleUNUserNotificationCenterDelegate
171+
{
172+
swizzleExistingSelector(
173+
[UNUserNotificationCenter.currentNotificationCenter.delegate class],
174+
@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:),
175+
[self class],
176+
@selector(userNotificationCenterLibraryB:willPresentNotification:withCompletionHandler:)
177+
);
178+
}
179+
-(void)userNotificationCenterLibraryB:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
180+
{
181+
selectorCalled = true;
182+
// Standard basic swizzling forwarder another library may have.
183+
if ([self respondsToSelector:@selector(userNotificationCenterLibraryA:willPresentNotification:withCompletionHandler:)])
184+
[self userNotificationCenterLibraryB:center willPresentNotification:notification withCompletionHandler:completionHandler];
185+
}
186+
@end
161187

162188

163189
@interface OneSignalUNUserNotificationCenterSwizzlingTest : XCTestCase
@@ -319,6 +345,41 @@ - (void)testDoubleSwizzleInfiniteLoop {
319345
[localOrignalDelegate userNotificationCenter:UNUserNotificationCenter.currentNotificationCenter willPresentNotification:[self createBasiciOSNotification] withCompletionHandler:^(UNNotificationPresentationOptions options) {}];
320346
}
321347

348+
- (void)testNotificationCenterSubClassIsNotSwizzledTwice {
349+
// 1. Create a new delegate and assign it
350+
id myDelegate = [UNUserNotificationCenterDelegateForInfiniteLoopTest new];
351+
UNUserNotificationCenter.currentNotificationCenter.delegate = myDelegate;
352+
353+
// 2. Create another Library's app delegate and assign it then swizzle
354+
id thierDelegate = [OtherUNNotificationLibraryASwizzler new];
355+
UNUserNotificationCenter.currentNotificationCenter.delegate = thierDelegate;
356+
[OtherUNNotificationLibraryASwizzler swizzleUNUserNotificationCenterDelegate];
357+
358+
// 3. Create another Library's app delegate subclass and assign it then swizzle
359+
id thierDelegateSubClass = [OtherUNNotificationLibraryBSubClassSwizzler new];
360+
UNUserNotificationCenter.currentNotificationCenter.delegate = thierDelegateSubClass;
361+
[OtherUNNotificationLibraryBSubClassSwizzler swizzleUNUserNotificationCenterDelegate];
362+
363+
// 4. Call something to confirm we don't get stuck in an infinite call loop
364+
id<UNUserNotificationCenterDelegate> delegate =
365+
UNUserNotificationCenter.currentNotificationCenter.delegate;
366+
[delegate
367+
userNotificationCenter:UNUserNotificationCenter.currentNotificationCenter
368+
willPresentNotification:[self createBasiciOSNotification]
369+
withCompletionHandler:^(UNNotificationPresentationOptions options) {}
370+
];
371+
372+
// 5. Ensure OneSignal's selector is called.
373+
XCTAssertEqual([OneSignalUNUserNotificationCenterOverrider
374+
callCountForSelector:@"onesignalUserNotificationCenter:willPresentNotification:withCompletionHandler:"], 1);
375+
376+
// 6. Ensure other library selector is still called too.
377+
XCTAssertTrue([OtherUNNotificationLibraryASwizzler selectorCalled]);
378+
379+
// 7. Ensure other library subclass selector is still called too.
380+
XCTAssertTrue([OtherUNNotificationLibraryBSubClassSwizzler selectorCalled]);
381+
}
382+
322383
- (void)testCompatibleWithOtherSwizzlerWhenSwapingBetweenNil {
323384
// 1. Create a new delegate and assign it
324385
id myAppDelegate = [UNUserNotificationCenterDelegateForInfiniteLoopWithAnotherSwizzlerTest new];

iOS_SDK/OneSignalSDK/UnitTests/UIApplicationDelegateSwizzlingTests.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ - (void)testAppDelegateSubClassIsNotSwizzledTwice {
466466
XCTAssertTrue([OtherLibraryASwizzler selectorCalled]);
467467

468468
// 7. Ensure other library subclass selector is still called too.
469-
XCTAssertTrue([OtherLibraryASwizzler selectorCalled]);
469+
XCTAssertTrue([OtherLibraryBSwizzlerSubClass selectorCalled]);
470470
}
471471

472472
- (void)testCompatibleWithOtherSwizzlerWhenSwapingBetweenNil {

0 commit comments

Comments
 (0)