Store Value for Different Remote Notifications and Badge TableView Cells

Discussion in 'iOS Programming' started by RagingGoat, Nov 11, 2014.

  1. RagingGoat macrumors 6502

    Joined:
    Jun 21, 2010
    #1
    My app receives push notifications when an RSS feed in my app is updated. When the app is launched from the notification, that RSS feed opens. When the app is in the foreground, an alert view is shown. If the app is not in the foreground and is not opened from the notification, a badge icon appears on the menu table view in the cell to open that RSS feed. When the cell is selected the app badge icon is reset and the icon in the cell is removed.

    I'm planning on adding notifications for things other than the RSS feed, such as member benefits, but I'm having trouble with showing the badge icon in the table view. My notificationType string is always null so there is never a badge icon placed in any cell in the menu table view.

    Code:
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    {
        UA_LINFO(@"Received remote notification: %@", userInfo);
        
        [[UAPush shared]appReceivedRemoteNotification:userInfo applicationState:application.applicationState];
        
        if (application.applicationState == UIApplicationStateActive) {
            [[UAPush shared] resetBadge];
        }
        
        if (application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground) {
            NSDictionary *apsInfo = [userInfo valueForKey:@"aps"];
            
            if ([apsInfo valueForKey:@"alert"] != NULL) {
                self.alert = [apsInfo valueForKey:@"alert"];
                
                if ([self.alert containsString:@"ACTION ALERT"]) {
                    self.notificationType = @"action alert";
                }
                else if ([self.alert containsString:@"MEMBER BENEFIT"]) {
                    self.notificationType = @"member benefit";
                }
            }
        }
    }
    
    In cellForRowAtIndexPath: in my menu table view.
    Code:
    KFBAppDelegate *appDelegate = (KFBAppDelegate *)[[UIApplication sharedApplication]delegate];
        badgeNumber = [NSString stringWithFormat:@"%ld", (long)[[UIApplication sharedApplication]applicationIconBadgeNumber]];
        actionAlertBadge = [JSCustomBadge customBadgeWithString:badgeNumber withStringColor:[UIColor whiteColor] withInsetColor:[UIColor redColor] withBadgeFrame:NO withBadgeFrameColor:[UIColor redColor] withScale:1.0 withShining:NO withShadow:NO];
        actionAlertBadge.frame = CGRectMake(83, 6, 30, 30);
        
        if ([badgeNumber isEqualToString:@"0"]) {
            actionAlertBadge.hidden = YES;
        }
        
        if (actionAlertBadge.hidden == NO) {
            if ([appDelegate.notificationType isEqualToString:@"action alert"]) {
                if (indexPath.section == 0) {
                    if (indexPath.row == 0) {
                        cell.accessoryView = actionAlertBadge;
                    }
                }
            }
            else if ([appDelegate.notificationType isEqualToString:@"member benefit"]) {
                if (indexPath.section == 0) {
                    if (indexPath.row == 5) {
                        cell.accessoryView = actionAlertBadge;
                    }
                }
            }
        }
    
     
  2. TheWatchfulOne, Nov 11, 2014
    Last edited: Nov 11, 2014

    TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #2
    Is there a reason why you are using the cell's accessoryView instead its imageView?

    Is your cells' accessoryView tapable? That is, are you going perform a different action when the accessory is tapped vs. when the row is selected?

    It seems to me that if you want to badge a table row (which I do in my app also) it's because you want to call the user's attention to that row. And it seems like the badge would be more easily noticeable on the left of the table instead of the right. I say that because the accessoryView is on the right and the imageview is on the left. And that would be my reason for using the imageView.

    It looks like you are numbering each badge too, is there are reason for that?

    After reading your post a little more carefully and taking a glance at the docs...

    Have you inspected your apsInfo dictionary to see what's in it?
    Have you verified your notification contains everything it should contain when received?

    I also found this nugget in the docs:

     
  3. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #3
    I am using the accessoryView because the imageView is already being used, and no, the accessoryView is not tappable.
     
  4. TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #4
    Very good. Are you allowed to add a small badge to the image in the imageView? (That's what I do.)

    I also noticed you are doing this in your cellForRowAtIndexPath: implementation:

    Code:
    KFBAppDelegate *appDelegate = (KFBAppDelegate *)[[UIApplication sharedApplication]delegate];
    I would make appDelegate a property on your tableView and then allocate and initialize it in viewDidLoad:

    You'd get better performance from your tableView (which may or may not be noticeable.)
     
  5. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #5
    I will do that. Any ideas why I'm not getting my desired result though. my notificationType string is always null so I never see the badge.
     
  6. TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #6
    I would start by setting a break point and inspect the notification when it gets received and make sure that everything is there that needs to be there. I.E. make sure the notification is getting sent correctly to begin with.
     
  7. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #7
    I am doing this in didFinishLaunchingWithOptions: and it works perfectly fine so I know the payload is ok.

    Code:
    // If application is launched due to  notification,present another view controller.
        UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
        
        if (notification)
        {
            NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
            NSDictionary *apsInfo = [userInfo valueForKey:@"aps"];
            
            NSString *alertMsg = @"";
            
            if ([apsInfo valueForKey:@"alert"] != NULL) {
                alertMsg = [apsInfo valueForKey:@"alert"];
                
                if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
                    if ([alertMsg containsString:@"ACTION ALERT"]) {
                        ActionAlertsViewController *rootView = [[ActionAlertsViewController alloc] initWithNibName:nil bundle:nil];
                        [rootView fetchEntries];
                        WebViewController *wvc = [[WebViewController alloc]init];
                        [rootView setWebViewController:wvc];
                        
                        KFBNavControllerViewController *navController = [[KFBNavControllerViewController alloc] initWithRootViewController:rootView];
                        navController.delegate = rootView;
                        
                        self.window.rootViewController = navController;
                    }
                    else if ([alertMsg containsString:@"MEMBER BENEFIT"]) {
                        MemberBenefits *rootView = [[MemberBenefits alloc] initWithNibName:nil bundle:nil];
                        
                        KFBNavControllerViewController *navController = [[KFBNavControllerViewController alloc] initWithRootViewController:rootView];
                        navController.delegate = rootView;
                        
                        self.window.rootViewController = navController;
                    }
                }
            }
    
     
  8. TheWatchfulOne, Nov 11, 2014
    Last edited: Nov 11, 2014

    TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #8
    Based on this info from the docs:

    I would expand your test:

    Code:
    [apsInfo valueForKey:@"alert"] != NULL
    I would also test whether resulting value from the above line is a string or a dictionary.

    edit #1:

    After thinking about it, your app probably would have crashed on this line:
    Code:
    [self.alert containsString:@"ACTION ALERT"]
    if self.alert were a dictionary.
    (I would still make sure self.alert is a string before calling that method on it.)

    I would check your message body in the debugger and make sure it really contains the strings you are checking for.

    You might add an else "clause" to your if/then block to handle the case where the message body does not contain any of the strings you are parsing for.

    Is the generation of the RSS feed under your control? Maybe check for typos at the source of the message?
     
  9. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #9
    I have verified that the alert contains one of the desired strings. If I'm using the app and a notification comes through, an alert view is displayed and I see in the console that didReceiveRemoteNotification: is called and my notificationType string is given the appropriate value. The problem seems to be didReceiveRemoteNotification: not being called in the scenarios I'm testing. I'm probably doing something wrong. For example, the user backgrounds the app by pressing the home button. A notification then comes through. The user taps the app icon to open the app. In this case didReceiveRemoteNotification: is never called. So, what should I do? Another scenario is that the user opens the app from multitasking by double tapping the home button. didReceiveRemoteNotifications: is still not called.
     
  10. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
  11. TheWatchfulOne, Nov 12, 2014
    Last edited: Nov 12, 2014

    TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #11
    Did the solution involve something like the following?
    (Code found on StackOverflow)

    Code:
    UIApplication *app = [UIApplication sharedApplication];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        bgTask = UIBackgroundTaskInvalid;
    };
    
    // do your background task
    
    NSLog(@"beginBG called");
    [app endBackgroundTask:bgTask];
    edit:

    And make sure you add the remote notifications key to the UIBackgroundModes array in the info.plist:

    From the docs:
    If you already solved it, please let us now what did the trick.
     
  12. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #12
    I take back what I said. I don't have it figured out. I tried
    Code:
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    and adding remote notifications to background modes but still nothing. didReceiveRemoteNotifications never gets called unless I'm actually using the app.
     
  13. TheWatchfulOne, Nov 12, 2014
    Last edited: Nov 12, 2014

    TheWatchfulOne macrumors 6502

    Joined:
    Jun 19, 2009
    #13
    OK, after reading the docs a little further, this method is only called in the foreground:

    Code:
    - (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    So you definately want the newer method which I see you have tried:

    Code:
    - (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
    And just in case you didn't already see this (I didn't until just now) here's some important info from the docs:

    You say didReceiveRemoteNotification: is not called and in the background and that is true. Have you verified that didReceiveRemoteNotification:fetchCompletionHandler: is not called in the background and if so, how? Where are you placing the breakpoint or NSLog?

    The docs say didReceiveRemoteNotification:fetchCompletionHandler: is called in the background and one would think that means all the code in it gets executed. You might try something like the following in your implementation of that method. I'm not even sure if this is proper/necessary/desirable, but I'd say give it a shot and see if it works:

    Code:
    UIApplication *app = [UIApplication sharedApplication];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        bgTask = UIBackgroundTaskInvalid;
    };
    
    // process your notification here
    
    NSLog(@"beginBG called"); //<- You can use a breakpoint to log messages to the console and actually I think it's better than NSLog
    
    [app endBackgroundTask:bgTask];
    edit:

    And silly me. I forgot to ask which iOS you are using.
     

Share This Page