Camelcasing & Underscoring Strings in Objective-C

Nowadays it’s all about API’s, 3rd party services and what not. In iOS universe this means connecting to remote web servers and fetching the data in [mostrly] JSON format. This is the place where the trouble starts. It’s all fine and dandy if you’re pals with web developer that coded the service and you don’t have to “map” the web parameter names to your core data attributes. But what if you’re not?

If you still haven’t recognized the problem from post’s title - I am talking about making web_app_url_parameters to pretty coreDataObjectAttributes and back. There are many heavy-weight solutions for this. I think RestKit is one of them, but I haven’t tried it so can not speak for it.

Me? I like to be dead simple. You know, SRP’n’stuff. I wrote a couple of categories for NSString class that does exactly that. With some options. So probably there goes my “dead simple”, but none the less. And as an extra bonus I did it using TDD (mad propz to Jon Reid and Graham Lee). To make it even simpler and faster I used only C string functions and no calls to Objective-C string/array methods.

First category is for camelcasing the strings. The category interface looks simple. Two methods with two options:

Category interface for NSString camelcasing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <Foundation/Foundation.h>

enum {
    WSStringCamelcaseOptionAttribute,
    WSStringCamelcaseOptionClass
};

typedef NSInteger WSStringCamelcaseOption;

@interface NSString (WSCamelcasing)

- (NSString *)camelcaseStringWithOptions:(WSStringCamelcaseOption)option;
- (NSString *)camelcaseString;

@end

And this is “the meat”. Options defaults to attribute camelcasing (i.e. leave first word unchanged and camelcase rest of the string).

Camelcasing underscored strings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#import "NSString+WSCamelcasing.h"

@implementation NSString (WSCamelcasing)

- (NSString *)camelcaseStringWithOptions:(WSStringCamelcaseOption)option {
    char *cString = (char *)[self UTF8String];
    size_t length = strlen(cString);
    char *dst = malloc(sizeof(*dst) * length);
    memset(dst, '\0', length);
    char *word;

    int offset = 0, dst_offset = 0;

    for (word = strtok(cString, "_");
         offset < length;
         word = strtok(&cString[offset], "_")
         )
    {
    if (
        (option == WSStringCamelcaseOptionClass) ||
        ((option == WSStringCamelcaseOptionAttribute) && (offset > 0))
    ) {
        if (word[0] > 96 && word[0] < 123) {
            word[0] -= 32;
      }
    }
        strncpy(&dst[dst_offset], word, strlen(word));
        dst_offset += strlen(word);
        offset += strlen(word) + 1;
    }

    return [NSString stringWithUTF8String:dst];
}


- (NSString *)camelcaseString {
    return [self camelcaseStringWithOptions:WSStringCamelcaseOptionAttribute];
}

@end

Now for making core data attributes web-api-friendly, second category contains method that converts the string from CamelCase to underscore_case. It has a single method with no options (probably because I couldn’t think of any).

Underscoring camelcased strings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#import <Foundation/Foundation.h>

@interface NSString (WSUnderscoring)

- (NSString *)underscoreString;

@end

@implementation NSString (WSUnderscoring)

- (NSString *)underscoreString {
    char *cString = (char *)[self UTF8String];
    size_t length = strlen(cString);
    char *dst = malloc(length * 2);
    memset(dst, '\0', length * 2);

    size_t i, offset;
    for (i=0, offset = 0; i<length; i++, offset++) {
        if ((cString[i] > 64) && (cString[i] < 91)) {
            cString[i] += 32;
            if (i && (cString[i-1] != '_')) {
                dst[offset] = '_';
                offset++;
            }

        }
        dst[offset] = cString[i];
    }

    return [NSString stringWithUTF8String:dst];
}

@end

I presented them here as a single code listing, but you are all grown ups and you can handle the separation of interface and implementation! Hope this makes your coding endevours more exciting and productive.

Comments