Several things:
1) Enums are designed for precisely what op wants. A lot of people override the first variable to be zero though:
typedef enum {
isStory = 0,
isMenu,
isChallenge,
} gameMode;
The reason for this is because there really isn't anything that says the enumeration will start at 0. It is kind of assumed and expected, just about every compiler out there does it, but I find that adding the "= 0" removes any chance for ambiguity. You now know that isStory is equivalent to 0, isMenu is now equivalent to 1, and isChallenge is equivalent to 2.
2) Don't #define NSStrings. Ever. This is really, really bad code practice.
The reason for this is because #define is a preprocessor statement. It is expanded BEFORE the code is compiled. Therefore, if you have the following:
#define kMyString @"MyKeyValue"
...
[someObject1 someMethod1:kMyString];
[someObject2 someMethod2:kMyString];
[someObject3 someMethod3:kMyString];
After being fed through the C preprocessor, you get this:
[someObject1 someMethod1

"MyKeyValue"];
[someObject2 someMethod2

"MyKeyValue"];
[someObject3 someMethod3

"MyKeyValue"];
Now you've got the string @"MyKeyValue" included in your program three times.
It is up to the compiler to be smart enough to optimize this away and recognize that these can all be replaced with a single instance of @"MyKeyValue". CLANG happens to be smart enough to do this, but I still think it's bad code practice.
If you need to define static strings, you should really follow chown33's post above this one. The advantage of using NSString * const kMyStaticString = @"SomeStupidString"; is that the compiler will automatically know that all instances of kMyStaticString should point to the same location in memory, because it's defined as a constant pointer to an NSString object.
Basically, if your compiler happens to be an idiot, using #define for strings can mean that the string itself is literally included in your binary however many times you use it in and around your code (which would be hundreds or thousands). If your compiler is an idiot and you're using the constant NSString pointer, then there's no ambiguity here at all- you don't need to rely on optimizations the compiler MIGHT employ. You are pointing to a single NSString object in memory and that is that, no matter how many times you reference it in your code the literal string value of that NSString will only be present in your application binary ONCE.
-SC