Search code examples
objective-cnsstringnslog

NSString logged to console with quotation marks, sometimes


Background

I have created a category on NSDictionary in order to make a copy, with a certain fixed modification applied to all keys (to use when mapping my objects against a web service):

@implementation NSDictionary (TransformKeys)

- (NSDictionary*) dictionaryByTransformingKeysWithBlock:(NSString* (^)(NSString* key))block
{
    NSMutableDictionary* transformedDictionary = [NSMutableDictionary new];

    for(NSString* key in [self allKeys]){

        // Transform the key string:
        NSString* transformedKey = block(key);

        // Copy value, using modified key in target dictionary:
        [transformedDictionary setObject:[self objectForKey:key]
                                  forKey:transformedKey];
    }

    return transformedDictionary;
}

@end

So I can map (say) @{ @"userName" : @"John"} to {@"UserName" : @"John"}

To use as part of said key transformations, I have also defined a category on NSString to raise the case of the first character:

@implementation NSString (Camel)

- (NSString*) stringByCapitalizingFirstCharacter
{
    if ([self length] == 0){
        return @"";
    }

    NSString* firstCharacter = [self substringToIndex:1];

    firstCharacter = [firstCharacter uppercaseString];

    if ([self length] == 1) {
        return firstCharacter;
    }

    NSString* theRest = [self substringFromIndex:1];

    return [firstCharacter stringByAppendingString:theRest];
}

@end

The keys, of course, are the object's property names obtained by introspection from the Objective-C runtime.


The Problem:

When I print to the console my dictionary -or its allKeys array, doesn't matter- using NSLog(), some of the keys are enclosed in quotation marks, while others are printed as-is.

The immediate cause seems to be that I am transforming my dictionary keys following two different rules, and they somehow print differently:

  1. If I transform my keys by capitalizing the first character, they print as-is:

    NSDictionary* convertedDictionary = [dictionary dictionaryByTransformingKeysWithBlock:^NSString *(NSString *key) {
    
        return [key stringByCapitalizingFirstCharacter];
    }];
    // -> Dictionary keys are printed to console as-is; i.e. UserName
    
  2. If I transform my keys by capitalizing and then appending a suffix, they are print enclosed in double quotation marks:

    NSDictionary* convertedDictionary = [dictionary dictionaryByTransformingKeysWithBlock:^NSString *(NSString *key) {
    
        NSString* capitalized = [key stringByCapitalizingFirstCharacter];
    
        return  [capitalized stringByAppendingString:@"__c"];
    }];
    // -> Dictionary keys are printed to console in quotation marks; i.e. "UserName"
    

I'm sure it's just a logging artifact, and the actual strings do not contain " in them, but it is a bit annoying.

Is there a way to unify the display of all keys in the console? Am I missing something?


Update

I converted my dictionary to JSON (which is what I'll ultimately end up doing, anyway) and tried logging that instead:

NSData* data = [NSJSONSerialization dataWithJSONObject:[dictionary allKeys]                                                    
                                               options:NSJSONWritingPrettyPrinted
                                                 error:nil];

NSLog(@"Keys: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

Now all the keys are uniformly enclosed in quotation marks (as expected), so this confirms that internally my data is in the expected format and no problems should arise due to the discrepancy observed in the log...


Solution

  • Although I cannot back this up with any documentation, from a quick test and a lot of time spending looking at logs, it seems that any key that contains non-alphanumeric characters (including spaces) will be logged with double quotes (and that's why you get it with your __ suffix)

    So, this:

    #import <Foundation/Foundation.h>
    
    int main(int argc, char *argv[]) {
        @autoreleasepool {
            NSDictionary *dict = @{
                @"simple": @"",
                @"camelCase": @"",
                @"camelCaseWith__suffix": @"",
                @"camelCaseWith space": @"",
                @"camelCaseWith/special": @""
            };
    
            NSLog(@"%@", dict);
        }
    }
    

    would log:

    {
        camelCase = "";
        "camelCaseWith space" = "";
        "camelCaseWith/special" = "";
        "camelCaseWith__suffix" = "";
        simple = "";
    }