|
75 | 75 |
|
76 | 76 | @implementation OneSignal |
77 | 77 |
|
78 | | -NSString* const ONESIGNAL_VERSION = @"020111"; |
| 78 | +NSString* const ONESIGNAL_VERSION = @"020112"; |
79 | 79 | static NSString* mSDKType = @"native"; |
80 | 80 | static BOOL coldStartFromTapOnNotification = NO; |
81 | 81 | static BOOL registeredWithApple = NO; //Has attempted to register for push notifications with Apple. |
@@ -1250,5 +1250,306 @@ + (void)syncHashedEmail:(NSString *)email { |
1250 | 1250 |
|
1251 | 1251 | @end |
1252 | 1252 |
|
| 1253 | +static BOOL checkIfInstanceOverridesSelector(Class instance, SEL selector) { |
| 1254 | + Class instSuperClass = [instance superclass]; |
| 1255 | + return [instance instanceMethodForSelector: selector] != [instSuperClass instanceMethodForSelector: selector]; |
| 1256 | +} |
| 1257 | + |
| 1258 | + |
| 1259 | +static Class getClassWithProtocolInHierarchy(Class searchClass, Protocol* protocolToFind) { |
| 1260 | + if (!class_conformsToProtocol(searchClass, protocolToFind)) { |
| 1261 | + if ([searchClass superclass] == nil) |
| 1262 | + return nil; |
| 1263 | + Class foundClass = getClassWithProtocolInHierarchy([searchClass superclass], protocolToFind); |
| 1264 | + if (foundClass) |
| 1265 | + return foundClass; |
| 1266 | + return searchClass; |
| 1267 | + } |
| 1268 | + return searchClass; |
| 1269 | +} |
| 1270 | + |
| 1271 | +static void injectSelector(Class newClass, SEL newSel, Class addToClass, SEL makeLikeSel) { |
| 1272 | + Method newMeth = class_getInstanceMethod(newClass, newSel); |
| 1273 | + IMP imp = method_getImplementation(newMeth); |
| 1274 | + const char* methodTypeEncoding = method_getTypeEncoding(newMeth); |
| 1275 | + BOOL successful = class_addMethod(addToClass, makeLikeSel, imp, methodTypeEncoding); |
| 1276 | + |
| 1277 | + if (!successful) { |
| 1278 | + class_addMethod(addToClass, newSel, imp, methodTypeEncoding); |
| 1279 | + newMeth = class_getInstanceMethod(addToClass, newSel); |
| 1280 | + Method orgMeth = class_getInstanceMethod(addToClass, makeLikeSel); |
| 1281 | + method_exchangeImplementations(orgMeth, newMeth); |
| 1282 | + } |
| 1283 | +} |
| 1284 | + |
| 1285 | +//Try to find out which class to inject to |
| 1286 | +static void injectToProperClass(SEL newSel, SEL makeLikeSel, NSArray* delegateSubclasses, Class myClass, Class delegateClass) { |
| 1287 | + |
| 1288 | + // Find out if we should inject in delegateClass or one of its subclasses. |
| 1289 | + //CANNOT use the respondsToSelector method as it returns TRUE to both implementing and inheriting a method |
| 1290 | + //We need to make sure the class actually implements the method (overrides) and not inherits it to properly perform the call |
| 1291 | + //Start with subclasses then the delegateClass |
| 1292 | + |
| 1293 | + for(Class subclass in delegateSubclasses) |
| 1294 | + if(checkIfInstanceOverridesSelector(subclass, makeLikeSel)) { |
| 1295 | + injectSelector(myClass, newSel, subclass, makeLikeSel); |
| 1296 | + return; |
| 1297 | + } |
| 1298 | + |
| 1299 | + //No subclass overrides the method, try to inject in delegate class |
| 1300 | + injectSelector(myClass, newSel, delegateClass, makeLikeSel); |
| 1301 | + |
| 1302 | +} |
| 1303 | + |
| 1304 | +static NSArray* ClassGetSubclasses(Class parentClass) { |
| 1305 | + |
| 1306 | + int numClasses = objc_getClassList(NULL, 0); |
| 1307 | + Class *classes = NULL; |
| 1308 | + |
| 1309 | + classes = (Class *)malloc(sizeof(Class) * numClasses); |
| 1310 | + numClasses = objc_getClassList(classes, numClasses); |
| 1311 | + |
| 1312 | + NSMutableArray *result = [NSMutableArray array]; |
| 1313 | + for (NSInteger i = 0; i < numClasses; i++) { |
| 1314 | + Class superClass = classes[i]; |
| 1315 | + do { |
| 1316 | + superClass = class_getSuperclass(superClass); |
| 1317 | + } while(superClass && superClass != parentClass); |
| 1318 | + |
| 1319 | + if (superClass == nil) continue; |
| 1320 | + [result addObject:classes[i]]; |
| 1321 | + } |
| 1322 | + |
| 1323 | + free(classes); |
| 1324 | + |
| 1325 | + return result; |
| 1326 | +} |
| 1327 | + |
| 1328 | +@interface OneSignalTracker () |
| 1329 | ++ (void)onFocus:(BOOL)toBackground; |
| 1330 | +@end |
| 1331 | + |
| 1332 | +@implementation UIApplication (Swizzling) |
| 1333 | +static Class delegateClass = nil; |
| 1334 | + |
| 1335 | +// Store an array of all UIAppDelegate subclasses to iterate over in cases where UIAppDelegate swizzled methods are not overriden in main AppDelegate |
| 1336 | +//But rather in one of the subclasses |
| 1337 | +static NSArray* delegateSubclasses = nil; |
| 1338 | + |
| 1339 | ++(Class)delegateClass { |
| 1340 | + return delegateClass; |
| 1341 | +} |
| 1342 | + |
| 1343 | +- (void)oneSignalDidRegisterForRemoteNotifications:(UIApplication*)app deviceToken:(NSData*)inDeviceToken { |
| 1344 | + |
| 1345 | + if([OneSignal app_id]) |
| 1346 | + [OneSignal didRegisterForRemoteNotifications:app deviceToken:inDeviceToken]; |
| 1347 | + |
| 1348 | + if ([self respondsToSelector:@selector(oneSignalDidRegisterForRemoteNotifications:deviceToken:)]) |
| 1349 | + [self oneSignalDidRegisterForRemoteNotifications:app deviceToken:inDeviceToken]; |
| 1350 | +} |
| 1351 | + |
| 1352 | +- (void)oneSignalDidFailRegisterForRemoteNotification:(UIApplication*)app error:(NSError*)err { |
| 1353 | + |
| 1354 | + if(err.code == 3000 && [(NSString*)[err.userInfo objectForKey:NSLocalizedDescriptionKey] containsString:@"no valid 'aps-environment'"]) { |
| 1355 | + //User did not enable push notification capability |
| 1356 | + [OneSignal setErrorNotificationType]; |
| 1357 | + [OneSignal onesignal_Log:ONE_S_LL_ERROR message:@"'Push Notification' capability not turned on. Make sure it is enabled by going to your Project Target -> Capability."]; |
| 1358 | + } |
| 1359 | + |
| 1360 | + else if([OneSignal app_id]) |
| 1361 | + [OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat: @"Error registering for Apple push notifications. Error: %@", err]]; |
| 1362 | + |
| 1363 | + if ([self respondsToSelector:@selector(oneSignalDidFailRegisterForRemoteNotification:error:)]) |
| 1364 | + [self oneSignalDidFailRegisterForRemoteNotification:app error:err]; |
| 1365 | +} |
| 1366 | + |
| 1367 | +- (void)oneSignalDidRegisterUserNotifications:(UIApplication*)application settings:(UIUserNotificationSettings*)notificationSettings { |
| 1368 | + |
| 1369 | + if([OneSignal app_id]) |
| 1370 | + [OneSignal updateNotificationTypes:notificationSettings.types]; |
| 1371 | + |
| 1372 | + if ([self respondsToSelector:@selector(oneSignalDidRegisterUserNotifications:settings:)]) |
| 1373 | + [self oneSignalDidRegisterUserNotifications:application settings:notificationSettings]; |
| 1374 | +} |
| 1375 | + |
| 1376 | + |
| 1377 | +// Notification opened! iOS 6 ONLY! |
| 1378 | +- (void)oneSignalReceivedRemoteNotification:(UIApplication*)application userInfo:(NSDictionary*)userInfo { |
| 1379 | + |
| 1380 | + if([OneSignal app_id]) |
| 1381 | + [OneSignal notificationOpened:userInfo isActive:[application applicationState] == UIApplicationStateActive]; |
| 1382 | + |
| 1383 | + if ([self respondsToSelector:@selector(oneSignalReceivedRemoteNotification:userInfo:)]) |
| 1384 | + [self oneSignalReceivedRemoteNotification:application userInfo:userInfo]; |
| 1385 | +} |
| 1386 | + |
| 1387 | +// User Tap on Notification while app was in background - OR - Notification received (silent or not, foreground or background) on iOS 7+ |
| 1388 | +- (void) oneSignalRemoteSilentNotification:(UIApplication*)application UserInfo:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult)) completionHandler { |
| 1389 | + |
| 1390 | + if([OneSignal app_id]) { |
| 1391 | + |
| 1392 | + //Call notificationAction if app is active -> not a silent notification but rather user tap on notification |
| 1393 | + //Unless iOS 10+ then call remoteSilentNotification instead. |
| 1394 | + if([UIApplication sharedApplication].applicationState == UIApplicationStateActive && userInfo[@"aps"][@"alert"]) |
| 1395 | + [OneSignal notificationOpened:userInfo isActive:YES]; |
| 1396 | + else [OneSignal remoteSilentNotification:application UserInfo:userInfo]; |
| 1397 | + |
| 1398 | + } |
| 1399 | + |
| 1400 | + if ([self respondsToSelector:@selector(oneSignalRemoteSilentNotification:UserInfo:fetchCompletionHandler:)]) { |
| 1401 | + [self oneSignalRemoteSilentNotification:application UserInfo:userInfo fetchCompletionHandler:completionHandler]; |
| 1402 | + return; |
| 1403 | + } |
| 1404 | + |
| 1405 | + //Make sure not a cold start from tap on notification (OS doesn't call didReceiveRemoteNotification) |
| 1406 | + if ([self respondsToSelector:@selector(oneSignalReceivedRemoteNotification:userInfo:)] && ![[OneSignal valueForKey:@"coldStartFromTapOnNotification"] boolValue]) |
| 1407 | + [self oneSignalReceivedRemoteNotification:application userInfo:userInfo]; |
| 1408 | + |
| 1409 | + completionHandler(UIBackgroundFetchResultNewData); |
| 1410 | +} |
| 1411 | + |
| 1412 | +- (void) oneSignalLocalNotificationOpened:(UIApplication*)application handleActionWithIdentifier:(NSString*)identifier forLocalNotification:(UILocalNotification*)notification completionHandler:(void(^)()) completionHandler { |
| 1413 | + |
| 1414 | + if([OneSignal app_id]) |
| 1415 | + [OneSignal processLocalActionBasedNotification:notification identifier:identifier]; |
| 1416 | + |
| 1417 | + if ([self respondsToSelector:@selector(oneSignalLocalNotificationOpened:handleActionWithIdentifier:forLocalNotification:completionHandler:)]) |
| 1418 | + [self oneSignalLocalNotificationOpened:application handleActionWithIdentifier:identifier forLocalNotification:notification completionHandler:completionHandler]; |
| 1419 | + |
| 1420 | + completionHandler(); |
| 1421 | +} |
| 1422 | + |
| 1423 | +- (void)oneSignalLocalNotificationOpened:(UIApplication*)application notification:(UILocalNotification*)notification { |
| 1424 | + |
| 1425 | + if([OneSignal app_id]) |
| 1426 | + [OneSignal processLocalActionBasedNotification:notification identifier:@"__DEFAULT__"]; |
| 1427 | + |
| 1428 | + if([self respondsToSelector:@selector(oneSignalLocalNotificationOpened:notification:)]) |
| 1429 | + [self oneSignalLocalNotificationOpened:application notification:notification]; |
| 1430 | +} |
| 1431 | + |
| 1432 | +- (void)oneSignalApplicationWillResignActive:(UIApplication*)application { |
| 1433 | + |
| 1434 | + if([OneSignal app_id]) |
| 1435 | + [OneSignalTracker onFocus:YES]; |
| 1436 | + |
| 1437 | + if ([self respondsToSelector:@selector(oneSignalApplicationWillResignActive:)]) |
| 1438 | + [self oneSignalApplicationWillResignActive:application]; |
| 1439 | +} |
| 1440 | + |
| 1441 | +- (void) oneSignalApplicationDidEnterBackground:(UIApplication*)application { |
| 1442 | + |
| 1443 | + if([OneSignal app_id]) |
| 1444 | + [OneSignalLocation onfocus:NO]; |
| 1445 | + |
| 1446 | + if ([self respondsToSelector:@selector(oneSignalApplicationDidEnterBackground:)]) |
| 1447 | + [self oneSignalApplicationDidEnterBackground:application]; |
| 1448 | +} |
| 1449 | + |
| 1450 | +- (void)oneSignalApplicationDidBecomeActive:(UIApplication*)application { |
| 1451 | + |
| 1452 | + if([OneSignal app_id]) { |
| 1453 | + [OneSignalTracker onFocus:NO]; |
| 1454 | + [OneSignalLocation onfocus:YES]; |
| 1455 | + } |
| 1456 | + |
| 1457 | + if ([self respondsToSelector:@selector(oneSignalApplicationDidBecomeActive:)]) |
| 1458 | + [self oneSignalApplicationDidBecomeActive:application]; |
| 1459 | +} |
| 1460 | + |
| 1461 | +-(void)oneSignalApplicationWillTerminate:(UIApplication *)application { |
| 1462 | + |
| 1463 | + if([OneSignal app_id]) |
| 1464 | + [OneSignalTracker onFocus:YES]; |
| 1465 | + |
| 1466 | + if ([self respondsToSelector:@selector(oneSignalApplicationWillTerminate:)]) |
| 1467 | + [self oneSignalApplicationWillTerminate:application]; |
| 1468 | +} |
| 1469 | + |
| 1470 | +#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending) |
| 1471 | ++ (void)load { |
| 1472 | + |
| 1473 | + if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"7.0")) |
| 1474 | + return; |
| 1475 | + |
| 1476 | + NSLog(@"Loaded"); |
| 1477 | + |
| 1478 | + //Swizzle App delegate |
| 1479 | + method_exchangeImplementations(class_getInstanceMethod(self, @selector(setDelegate:)), class_getInstanceMethod(self, @selector(setOneSignalDelegate:))); |
| 1480 | +} |
| 1481 | + |
| 1482 | +- (void) setOneSignalDelegate:(id<UIApplicationDelegate>)delegate { |
| 1483 | + |
| 1484 | + if (delegateClass) { |
| 1485 | + [self setOneSignalDelegate:delegate]; |
| 1486 | + return; |
| 1487 | + } |
| 1488 | + |
| 1489 | + delegateClass = getClassWithProtocolInHierarchy([delegate class], @protocol(UIApplicationDelegate)); |
| 1490 | + delegateSubclasses = ClassGetSubclasses(delegateClass); |
| 1491 | + |
| 1492 | + injectToProperClass(@selector(oneSignalRemoteSilentNotification:UserInfo:fetchCompletionHandler:), |
| 1493 | + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:), delegateSubclasses, self.class, delegateClass); |
| 1494 | + |
| 1495 | + injectToProperClass(@selector(oneSignalLocalNotificationOpened:handleActionWithIdentifier:forLocalNotification:completionHandler:), |
| 1496 | + @selector(application:handleActionWithIdentifier:forLocalNotification:completionHandler:), delegateSubclasses, self.class, delegateClass); |
| 1497 | + |
| 1498 | + injectToProperClass(@selector(oneSignalDidFailRegisterForRemoteNotification:error:), |
| 1499 | + @selector(application:didFailToRegisterForRemoteNotificationsWithError:), delegateSubclasses, self.class, delegateClass); |
| 1500 | + |
| 1501 | + injectToProperClass(@selector(oneSignalDidRegisterUserNotifications:settings:), |
| 1502 | + @selector(application:didRegisterUserNotificationSettings:), delegateSubclasses, self.class, delegateClass); |
| 1503 | + |
| 1504 | + if (NSClassFromString(@"CoronaAppDelegate")) { |
| 1505 | + [self setOneSignalDelegate:delegate]; |
| 1506 | + return; |
| 1507 | + } |
| 1508 | + |
| 1509 | + injectToProperClass(@selector(oneSignalReceivedRemoteNotification:userInfo:), @selector(application:didReceiveRemoteNotification:), delegateSubclasses, self.class, delegateClass); |
| 1510 | + |
| 1511 | + injectToProperClass(@selector(oneSignalDidRegisterForRemoteNotifications:deviceToken:), @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:), delegateSubclasses, self.class, delegateClass); |
| 1512 | + |
| 1513 | + injectToProperClass(@selector(oneSignalLocalNotificationOpened:notification:), @selector(application:didReceiveLocalNotification:), delegateSubclasses, self.class, delegateClass); |
| 1514 | + |
| 1515 | + injectToProperClass(@selector(oneSignalApplicationWillResignActive:), @selector(applicationWillResignActive:), delegateSubclasses, self.class, delegateClass); |
| 1516 | + |
| 1517 | + //Required for background location |
| 1518 | + injectToProperClass(@selector(oneSignalApplicationDidEnterBackground:), @selector(applicationDidEnterBackground:), delegateSubclasses, self.class, delegateClass); |
| 1519 | + |
| 1520 | + injectToProperClass(@selector(oneSignalApplicationDidBecomeActive:), @selector(applicationDidBecomeActive:), delegateSubclasses, self.class, delegateClass); |
| 1521 | + |
| 1522 | + //Used to track how long the app has been closed |
| 1523 | + injectToProperClass(@selector(oneSignalApplicationWillTerminate:), @selector(applicationWillTerminate:), delegateSubclasses, self.class, delegateClass); |
| 1524 | + |
| 1525 | + |
| 1526 | + /* iOS 10.0: UNUserNotificationCenterDelegate instead of UIApplicationDelegate for methods handling opening app from notification |
| 1527 | + Make sure AppDelegate does not conform to this protocol */ |
| 1528 | +#if XC8_AVAILABLE |
| 1529 | + if([OneSignalHelper isiOS10Plus]) |
| 1530 | + [OneSignalHelper conformsToUNProtocol]; |
| 1531 | +#endif |
| 1532 | + |
| 1533 | + [self setOneSignalDelegate:delegate]; |
| 1534 | +} |
| 1535 | + |
| 1536 | ++(UIViewController*)topmostController:(UIViewController*)base { |
| 1537 | + |
| 1538 | + UINavigationController *baseNav = (UINavigationController*) base; |
| 1539 | + UITabBarController *baseTab = (UITabBarController*) base; |
| 1540 | + if (baseNav) |
| 1541 | + return [UIApplication topmostController:baseNav.visibleViewController]; |
| 1542 | + |
| 1543 | + else if (baseTab.selectedViewController) |
| 1544 | + return [UIApplication topmostController:baseTab.selectedViewController]; |
| 1545 | + |
| 1546 | + else if (base.presentedViewController) |
| 1547 | + return [UIApplication topmostController:baseNav.presentedViewController]; |
| 1548 | + |
| 1549 | + return base; |
| 1550 | +} |
| 1551 | + |
| 1552 | +@end |
| 1553 | + |
1253 | 1554 | #pragma clang diagnostic pop |
1254 | 1555 | #pragma clang diagnostic pop |
0 commit comments