Changing Icons with Push Notifications
Contents
Set Up the Icons
For this new API, you unfortunately cannot use your XCAssets folder. You instead have to import icon assets using an older technique, by creating a group under Resources.Configure Your Property List
Inside your App'sInfo.plist
file, add the following config to declare your main icon and it's alternatives. The file name should not include the @2x/@3x and filetype suffix.
Trigger Icon Changes
Now, the final step is to configure your app to change the icon in a reaction to a push. We'll use Key-Value Payloads to send down the alternate icon's key (the key for the dictionary above) and use the standard api for receiving pushes to react.iOS (Objective-C)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSString *iconName = userInfo[@"icon_name"];
if (!iconName) {
completionHandler(UIBackgroundFetchResultNoData);
return;
}
// We found this delay necessary in testing
[self delay:1.0 closure:^{
[self changeIcon:iconName];
completionHandler(UIBackgroundFetchResultNewData);
}];
}
- (void)changeIcon:(NSString *)iconName {
if (@available(iOS 10.3, *)) {
if ([UIApplication sharedApplication].supportsAlternateIcons) {
[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Error setting icon: %@", error);
}
}];
}
else {
NSLog(@"I cannot change icons");
}
}
}
- (void)delay:(double)delay closure:(void(^)())closure {
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
dispatch_after(when, dispatch_get_main_queue(), closure);
}
iOS (Swift)
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let iconName = userInfo["icon_name"] as! String? else {
completionHandler(.noData)
return
}
// We found this delay necessary in testing
delay(1.0, closure: {
self.changeIcon(iconName: iconName)
completionHandler(.newData)
})
}
func changeIcon(iconName: String) {
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
UIApplication.shared.setAlternateIconName(iconName) { (err:Error?) in
print("Error setting icon: \(String(describing: err))")
}
} else {
print("I cannot change icons")
}
}
}
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}