0

I would like to convert a UIRemoteNotificationType into a string for use in analytics tracking. Something like "badge:sound:alert". What would be the preferred method for doing this using the latest CLANG Objective-C language features available in Xcode 5?

I've seen a number of other questions on doing single value NSEnum values using various techniques, notably here, and here. However, none of these discuss a solution for NS_OPTION based enums which contain multiple bitmask values.

My initial thought is that I'll need an NSDictionary to map the values and NSArray to collect them after iterating, is there a more elegant way to approach this?

Community
  • 1
  • 1
Mark Edington
  • 6,484
  • 3
  • 33
  • 33

2 Answers2

2

Here is the solution I came up with, reasonably terse, but still type specific and brittle to future expansion:

from UIApplication.h typedef NS_OPTIONS(NSUInteger, UIRemoteNotificationType) { UIRemoteNotificationTypeNone = 0, UIRemoteNotificationTypeBadge = 1 << 0, UIRemoteNotificationTypeSound = 1 << 1, UIRemoteNotificationTypeAlert = 1 << 2, UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3, } NS_ENUM_AVAILABLE_IOS(3_0);

NSString* remoteNotificationTypesToString(UIRemoteNotificationType notificationTypes)
{
    NSArray *remoteNotificationTypeStrs = @[@"Badge", @"Sound", @"Alert", @"NewsStand"];
    NSMutableArray *enabledNotificationTypes = [[NSMutableArray alloc] init];

    #define kBitsUsedByUIRemoteNotificationType 4
    for (NSUInteger i=0; i < kBitsUsedByUIRemoteNotificationType; i++) {
        NSUInteger enumBitValueToCheck = 1 << i;
        if (notificationTypes & enumBitValueToCheck)
            [enabledNotificationTypes addObject:[remoteNotificationTypeStrs objectAtIndex:i]];
    }

    NSString *result = enabledNotificationTypes.count > 0 ?
                       [enabledNotificationTypes componentsJoinedByString:@":"] :
                       @"NotificationsDisabled";

    return result;
}

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
NSString *notificationTypesStr = remoteNotificationTypesToString(notificationTypes);
NSLog(@"Notification types: %@", notificationTypesStr);
Mark Edington
  • 6,484
  • 3
  • 33
  • 33
  • Hmm, is there a specific reason why you need to store the NSString's in an array? Creating arrays comes with overhead, and if you can avoid it -- especially in the memory constrained environment on mobile devices -- it's best to stay away. – rambo Dec 14 '13 at 01:36
  • I'm using an NSArray for the string literals because that is what the @[] shorthand creates. I'm using an NSMutableArray to collect the string literals because it has the `componentsJoinedByString` method which handles the concatenation for me. Honestly, I find the suggestion to avoid using an NSArray for this solution a bit odd. If you have some sort of authoritative Apple iOS developer resource (preferably official) to back this up I would love read it. – Mark Edington Dec 16 '13 at 20:03
  • Sorry for any confusion Mark. Your solution is a totally valid way of doing it, and I was just curious as to your methodology. As mobile environments are generally considered memory-constrained, my suggestion was not to use memory that you don't need (in this case, the NSArray and NSMutableArray). So I was just curious as to whether there was an explicit reason to store NSStrings in an NSArray (scalability, some other implementation detail in your code, etc.) – rambo Dec 16 '13 at 20:14
  • 1
    Since these are both local variables and released by ARC as soon as the function exits, I'm not clear on how this would create any lasting effect on the memory available to the system or the application. There is a tiny bit of CPU overhead for the object allocation on the heap, but otherwise my expectation is this won't be much different that using stack based local C data structure. My preference is to leverage all the niceties of the Objective-C language and runtime which is why I worded my question as I did. I appreciate the feedback. – Mark Edington Dec 16 '13 at 21:45
0

In terms of elegance, I don't think there's a way (yet) to get the names of options in NS_ENUM or NS_OPTIONS, since they are just macros to the underlying C enum and don't add any OOP niceties. It would be nice if there was some NSEnum/NSOptions class wrapper that defined a method "getNames" for the underlying enum to do what you suggest, but unfortunately it doesn't exist yet.

So, back to reality here, the NSDictionary approach will definitely work, but how about just writing a simple helper method that does some bitwise operations and returns your NSString? So if you have some NSInteger that represents your bitmask, you could write a method like so:

- (NSString *)optionsToSerializedString:(NSInteger)options{
    NSString *str = @"";

    if (options & UIRemoteNotificationTypeBadge) {
        str = [str stringByAppendingString:@"badge:"];
    }
    if (options & UIRemoteNotificationTypeAlert) {
        str = [str stringByAppendingString:@"alert:"];
    }
    //etc. etc. for other cases

    return str;
}

Note that this won't quite get it to the format that you'd like in your question ("badge:sound:alert"), but I'll leave that to you!

rambo
  • 369
  • 1
  • 13