Assuming that I have a typedef declared in my .h file as such:
typedef enum {
JSON,
XML,
Atom,
RSS
} FormatType;
I would like to build a function that converts the numeric value of the typedef to a string. For example, if the message [self toString:JSON]
was sent; it would return 'JSON'.
The function would look something like this:
-(NSString *) toString:(FormatType)formatType {
//need help here
return [];
}
Incidentally, if I try this syntax
[self toString:FormatType.JSON];
to pass the typedef value to the method, I get an error. What am I missing?
This question is related to
c
objective-c
enums
typedef
I would use the compiler's # string token (along with macros to make it all more compact):
#define ENUM_START \
NSString* ret; \
switch(value) {
#define ENUM_CASE(evalue) \
case evalue: \
ret = @#evalue; \
break;
#define ENUM_END \
} \
return ret;
NSString*
_CvtCBCentralManagerStateToString(CBCentralManagerState value)
{
ENUM_START
ENUM_CASE(CBCentralManagerStateUnknown)
ENUM_CASE(CBCentralManagerStateResetting)
ENUM_CASE(CBCentralManagerStateUnsupported)
ENUM_CASE(CBCentralManagerStateUnauthorized)
ENUM_CASE(CBCentralManagerStatePoweredOff)
ENUM_CASE(CBCentralManagerStatePoweredOn)
ENUM_END
}
I use a variation on Barry Walk's answer, that in order of importance:
EG:
- (NSString*)describeFormatType:(FormatType)formatType {
switch(formatType) {
case JSON:
return @"JSON";
case XML:
return @"XML";
case Atom:
return @"Atom";
case RSS:
return @"RSS";
}
[NSException raise:NSInvalidArgumentException format:@"The given format type number, %ld, is not known.", formatType];
return nil; // Keep the compiler happy - does not understand above line never returns!
}
You can't do it easily. In C and Objective-C, enums are really just glorified integer constants. You'll have to generate a table of names yourself (or with some preprocessor abuse). For example:
// In a header file
typedef enum FormatType {
JSON,
XML,
Atom,
RSS
} FormatType;
extern NSString * const FormatType_toString[];
// In a source file
// initialize arrays with explicit indices to make sure
// the string match the enums properly
NSString * const FormatType_toString[] = {
[JSON] = @"JSON",
[XML] = @"XML",
[Atom] = @"Atom",
[RSS] = @"RSS"
};
...
// To convert enum to string:
NSString *str = FormatType_toString[theEnumValue];
The danger of this approach is that if you ever change the enum, you have to remember to change the array of names. You can solve this problem with some preprocessor abuse, but it's tricky and ugly.
Also note that this assumes you have a valid enum constant. If you have an integer value from an untrusted source, you additionally need to do a check that your constant is valid, e.g. by including a "past max" value in your enum, or by checking if it's less than the array length, sizeof(FormatType_toString) / sizeof(FormatType_toString[0])
.
Here is working -> https://github.com/ndpiparava/ObjcEnumString
//1st Approach
#define enumString(arg) (@""#arg)
//2nd Approach
+(NSString *)secondApproach_convertEnumToString:(StudentProgressReport)status {
char *str = calloc(sizeof(kgood)+1, sizeof(char));
int goodsASInteger = NSSwapInt((unsigned int)kgood);
memcpy(str, (const void*)&goodsASInteger, sizeof(goodsASInteger));
NSLog(@"%s", str);
NSString *enumString = [NSString stringWithUTF8String:str];
free(str);
return enumString;
}
//Third Approcah to enum to string
NSString *const kNitin = @"Nitin";
NSString *const kSara = @"Sara";
typedef NS_ENUM(NSUInteger, Name) {
NameNitin,
NameSara,
};
+ (NSString *)thirdApproach_convertEnumToString :(Name)weekday {
__strong NSString **pointer = (NSString **)&kNitin;
pointer +=weekday;
return *pointer;
}
First of all, with regards to FormatType.JSON: JSON is not a member of FormatType, it's a possible value of the type. FormatType isn't even a composite type — it's a scalar.
Second, the only way to do this is to create a mapping table. The more common way to do this in Objective-C is to create a series of constants referring to your "symbols", so you'd have NSString *FormatTypeJSON = @"JSON"
and so on.
Depending on your needs, you could alternatively use compiler directives to simulate the behaviour you are looking for.
#define JSON @"JSON"
#define XML @"XML"
#define Atom @"Atom"
#define RSS @"RSS"
Just remember the usual compiler shortcomings, (not type safe, direct copy-paste makes source file larger)
the following provides a solution such that to add a new enum requires only a one-line edit, similar work to adding a single line in an enum {} list.
//------------------------------------------------------------------------------
// enum to string example
#define FOR_EACH_GENDER(tbd) \
tbd(GENDER_MALE) \
tbd(GENDER_FEMALE) \
tbd(GENDER_INTERSEX) \
#define ONE_GENDER_ENUM(name) name,
enum
{
FOR_EACH_GENDER(ONE_GENDER_ENUM)
MAX_GENDER
};
#define ONE_GENDER(name) #name,
static const char *enumGENDER_TO_STRING[] =
{
FOR_EACH_GENDER(ONE_GENDER)
};
// access string name with enumGENDER_TO_STRING[value]
// or, to be safe converting from a untrustworthy caller
static const char *enumGenderToString(unsigned int value)
{
if (value < MAX_GENDER)
{
return enumGENDER_TO_STRING[value];
}
return NULL;
}
static void printAllGenders(void)
{
for (int ii = 0; ii < MAX_GENDER; ii++)
{
printf("%d) gender %s\n", ii, enumGENDER_TO_STRING[ii]);
}
}
//------------------------------------------------------------------------------
// you can assign an arbitrary value and/or information to each enum,
#define FOR_EACH_PERSON(tbd) \
tbd(2, PERSON_FRED, "Fred", "Weasley", GENDER_MALE, 12) \
tbd(4, PERSON_GEORGE, "George", "Weasley", GENDER_MALE, 12) \
tbd(6, PERSON_HARRY, "Harry", "Potter", GENDER_MALE, 10) \
tbd(8, PERSON_HERMIONE, "Hermione", "Granger", GENDER_FEMALE, 10) \
#define ONE_PERSON_ENUM(value, ename, first, last, gender, age) ename = value,
enum
{
FOR_EACH_PERSON(ONE_PERSON_ENUM)
};
typedef struct PersonInfoRec
{
int value;
const char *ename;
const char *first;
const char *last;
int gender;
int age;
} PersonInfo;
#define ONE_PERSON_INFO(value, ename, first, last, gender, age) \
{ ename, #ename, first, last, gender, age },
static const PersonInfo personInfo[] =
{
FOR_EACH_PERSON(ONE_PERSON_INFO)
{ 0, NULL, NULL, NULL, 0, 0 }
};
// note: if the enum values are not sequential, you need another way to lookup
// the information besides personInfo[ENUM_NAME]
static void printAllPersons(void)
{
for (int ii = 0; ; ii++)
{
const PersonInfo *pPI = &personInfo[ii];
if (!pPI->ename)
{
break;
}
printf("%d) enum %-15s %8s %-8s %13s %2d\n",
pPI->value, pPI->ename, pPI->first, pPI->last,
enumGenderToString(pPI->gender), pPI->age);
}
}
My solution:
edit: I've added even a better solution at the end, using Modern Obj-C
1.
Put names as keys in an array.
Make sure the indexes are the appropriate enums, and in the right order (otherwise exception).
note: names is a property synthesized as *_names*;
code was not checked for compilation, but I used the same technique in my app.
typedef enum {
JSON,
XML,
Atom,
RSS
} FormatType;
+ (NSArray *)names
{
static NSMutableArray * _names = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_names = [NSMutableArray arrayWithCapacity:4];
[_names insertObject:@"JSON" atIndex:JSON];
[_names insertObject:@"XML" atIndex:XML];
[_names insertObject:@"Atom" atIndex:Atom];
[_names insertObject:@"RSS" atIndex:RSS];
});
return _names;
}
+ (NSString *)nameForType:(FormatType)type
{
return [[self names] objectAtIndex:type];
}
//
2.
Using Modern Obj-C you we can use a dictionary to tie descriptions to keys in the enum.
Order DOES NOT matter.
typedef NS_ENUM(NSUInteger, UserType) {
UserTypeParent = 0,
UserTypeStudent = 1,
UserTypeTutor = 2,
UserTypeUnknown = NSUIntegerMax
};
@property (nonatomic) UserType type;
+ (NSDictionary *)typeDisplayNames
{
return @{@(UserTypeParent) : @"Parent",
@(UserTypeStudent) : @"Student",
@(UserTypeTutor) : @"Tutor",
@(UserTypeUnknown) : @"Unknown"};
}
- (NSString *)typeDisplayName
{
return [[self class] typeDisplayNames][@(self.type)];
}
Usage (in a class instance method):
NSLog(@"%@", [self typeDisplayName]);
@pixel added the most brilliant answer here: https://stackoverflow.com/a/24255387/1364257 Please, upvote him!
He uses the neat X macro from the 1960's. (I've changed his code a bit for the modern ObjC)
#define X(a, b, c) a b,
enum ZZObjectType {
XXOBJECTTYPE_TABLE
};
typedef NSUInteger TPObjectType;
#undef X
#define XXOBJECTTYPE_TABLE \
X(ZZObjectTypeZero, = 0, @"ZZObjectTypeZero") \
X(ZZObjectTypeOne, , @"ZZObjectTypeOne") \
X(ZZObjectTypeTwo, , @"ZZObjectTypeTwo") \
X(ZZObjectTypeThree, , @"ZZObjectTypeThree")
+ (NSString*)nameForObjectType:(ZZObjectType)objectType {
#define X(a, b, c) @(a):c,
NSDictionary *dict = @{XXOBJECTTYPE_TABLE};
#undef X
return dict[objectType];
}
That's it. Clean and neat. Thanks to @pixel! https://stackoverflow.com/users/21804/pixel
I had a large enumerated type I wanted to convert it into an NSDictionary
lookup. I ended up using sed
from OSX terminal as:
$ sed -E 's/^[[:space:]]{1,}([[:alnum:]]{1,}).*$/ @(\1) : @"\1",/g' ObservationType.h
which can be read as: 'capture the first word on the line and output @(word) : @"word",'
This regex converts the enum in a header file named 'ObservationType.h' which contains:
typedef enum : int {
ObservationTypePulse = 1,
ObservationTypeRespRate = 2,
ObservationTypeTemperature = 3,
.
.
}
into something like:
@(ObservationTypePulse) : @"ObservationTypePulse",
@(ObservationTypeRespRate) : @"ObservationTypeRespRate",
@(ObservationTypeTemperature) : @"ObservationTypeTemperature",
.
.
which can then be wrapped in a method using modern objective-c syntax @{ }
(as explained by @yar1vn above) to create a NSDictionary
lookup :
-(NSDictionary *)observationDictionary
{
static NSDictionary *observationDictionary;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
observationDictionary = [[NSDictionary alloc] initWithDictionary:@{
@(ObservationTypePulse) : @"ObservationTypePulse",
@(ObservationTypeRespRate) : @"ObservationTypeRespRate",
.
.
}];
});
return observationDictionary;
}
The dispatch_once
boiler-plate is just to ensure that the static variable is initialised in a thread-safe manner.
Note: I found the sed regex expression on OSX odd - when I tried to use +
to match 'one or more' it didn't work and had to resort to using {1,}
as a replacement
I like the #define
way of doing this:
// Place this in your .h file, outside the @interface block
typedef enum {
JPG,
PNG,
GIF,
PVR
} kImageType;
#define kImageTypeArray @"JPEG", @"PNG", @"GIF", @"PowerVR", nil
// Place this in the .m file, inside the @implementation block
// A method to convert an enum to string
-(NSString*) imageTypeEnumToString:(kImageType)enumVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
return [imageTypeArray objectAtIndex:enumVal];
}
source (source no longer available)
Given an enum definition like:
typedef NS_ENUM(NSInteger, AssetIdentifier) {
Isabella,
William,
Olivia
};
We can define a macro to convert an enum value to its corresponding string, as shown below.
#define AssetIdentifier(asset) \
^(AssetIdentifier identifier) { \
switch (identifier) { \
case asset: \
default: \
return @#asset; \
} \
}(asset)
The switch
statement used in the block is for type checking, and also to get the auto-complete support in Xcode.
Every answer here basically says the same thing, create a regular enum and then use a custom getter to switch between strings.
I employ a much simpler solution that is faster, shorter, and cleaner—using Macros!
#define kNames_allNames ((NSArray <NSString *> *)@[@"Alice", @"Bob", @"Eve"])
#define kNames_alice ((NSString *)kNames_allNames[0])
#define kNames_bob ((NSString *)kNames_allNames[1])
#define kNames_eve ((NSString *)kNames_allNames[2])
Then you can simply start to type kNam...
and autocomplete will display the lists you desire!
Additionally, if you want to handle logic for all the names at once you can simply fast enumerate the literal array in order, as follows:
for (NSString *kName in kNames_allNames) {}
Lastly, the NSString casting in the macros ensures behavior similar to typedef!
Enjoy!
I made a sort of mix of all solutions found on this page to create mine, it's a kind of object oriented enum extension or something.
In fact if you need more than just constants (i.e. integers), you probably need a model object (We're all talking about MVC, right?)
Just ask yourself the question before using this, am I right, don't you, in fact, need a real model object, initialized from a webservice, a plist, an SQLite database or CoreData ?
Anyway here comes the code (MPI is for "My Project Initials", everybody use this or their name, it seems) :
MyWonderfulType.h
:
typedef NS_ENUM(NSUInteger, MPIMyWonderfulType) {
MPIMyWonderfulTypeOne = 1,
MPIMyWonderfulTypeTwo = 2,
MPIMyWonderfulTypeGreen = 3,
MPIMyWonderfulTypeYellow = 4,
MPIMyWonderfulTypePumpkin = 5
};
#import <Foundation/Foundation.h>
@interface MyWonderfulType : NSObject
+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType;
+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType;
@end
And MyWonderfulType.m
:
#import "MyWonderfulType.h"
@implementation MyWonderfulType
+ (NSDictionary *)myWonderfulTypeTitles
{
return @{
@(MPIMyWonderfulTypeOne) : @"One",
@(MPIMyWonderfulTypeTwo) : @"Two",
@(MPIMyWonderfulTypeGreen) : @"Green",
@(MPIMyWonderfulTypeYellow) : @"Yellow",
@(MPIMyWonderfulTypePumpkin) : @"Pumpkin"
};
}
+ (NSDictionary *)myWonderfulTypeURLs
{
return @{
@(MPIMyWonderfulTypeOne) : @"http://www.theone.com",
@(MPIMyWonderfulTypeTwo) : @"http://www.thetwo.com",
@(MPIMyWonderfulTypeGreen) : @"http://www.thegreen.com",
@(MPIMyWonderfulTypeYellow) : @"http://www.theyellow.com",
@(MPIMyWonderfulTypePumpkin) : @"http://www.thepumpkin.com"
};
}
+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType {
return [MPIMyWonderfulType myWonderfulTypeTitles][@(wonderfulType)];
}
+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType {
return [MPIMyWonderfulType myWonderfulTypeURLs][@(wonderfulType)];
}
@end
Improved @yar1vn answer by dropping string dependency:
#define VariableName(arg) (@""#arg)
typedef NS_ENUM(NSUInteger, UserType) {
UserTypeParent = 0,
UserTypeStudent = 1,
UserTypeTutor = 2,
UserTypeUnknown = NSUIntegerMax
};
@property (nonatomic) UserType type;
+ (NSDictionary *)typeDisplayNames
{
return @{@(UserTypeParent) : VariableName(UserTypeParent),
@(UserTypeStudent) : VariableName(UserTypeStudent),
@(UserTypeTutor) : VariableName(UserTypeTutor),
@(UserTypeUnknown) : VariableName(UserTypeUnknown)};
}
- (NSString *)typeDisplayName
{
return [[self class] typeDisplayNames][@(self.type)];
}
Thus when you'll change enum entry name corresponding string will be changed. Useful in case if you are not going to show this string to user.
Many answers all fairly good.
If you are after a generic, Objective C solution that uses some macros...
Key feature is it uses the enum as an index into a static array of NSString constants. the array itself is wrapped into a function to make it more like the suite of NSStringFromXXX functions prevalent in the Apple APIs.
you will need to #import "NSStringFromEnum.h"
found here
http://pastebin.com/u83RR3Vk
[EDIT]
also needs #import "SW+Variadic.h"
found here http://pastebin.com/UEqTzYLf
Example 1 : completely define a NEW enum typedef, with string converters.
in myfile.h
#import "NSStringFromEnum.h"
#define define_Dispatch_chain_cmd(enum)\
enum(chain_done,=0)\
enum(chain_entry)\
enum(chain_bg)\
enum(chain_mt)\
enum(chain_alt)\
enum(chain_for_c)\
enum(chain_while)\
enum(chain_continue_for)\
enum(chain_continue_while)\
enum(chain_break_for)\
enum(chain_break_while)\
enum(chain_previous)\
enum(chain_if)\
enum(chain_else)\
interface_NSString_Enum_DefinitionAndConverters(Dispatch_chain_cmd)
in myfile.m:
#import "myfile.h"
implementation_NSString_Enum_Converters(Dispatch_chain_cmd)
to use :
NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);
NSStringFromEnumDispatch_chain_cmd(chain_for_c)
returns @"chain_for_c"
enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);
enumDispatch_chain_cmdFromNSString(@"chain_previous")
returns chain_previous
Example 2: provide conversion routines for an existing enum also demonstrates using a settings string, and renaming the typename used in the functions.
in myfile.h
#import "NSStringFromEnum.h"
#define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask
interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)
in myfile.m:
// we can put this in the .m file as we are not defining a typedef, just the strings.
#define define_CAEdgeAntialiasingMask(enum)\
enum(kCALayerLeftEdge)\
enum(kCALayerRightEdge)\
enum(kCALayerBottomEdge)\
enum(kCALayerTopEdge)
implementation_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)
I combined several approaches here. I like the idea of the preprocessor and the indexed list.
There's no extra dynamic allocation, and because of the inlining the compiler might be able to optimize the lookup.
typedef NS_ENUM(NSUInteger, FormatType) { FormatTypeJSON = 0, FormatTypeXML, FormatTypeAtom, FormatTypeRSS, FormatTypeCount };
NS_INLINE NSString *FormatTypeToString(FormatType t) {
if (t >= FormatTypeCount)
return nil;
#define FormatTypeMapping(value) [value] = @#value
NSString *table[FormatTypeCount] = {FormatTypeMapping(FormatTypeJSON),
FormatTypeMapping(FormatTypeXML),
FormatTypeMapping(FormatTypeAtom),
FormatTypeMapping(FormatTypeRSS)};
#undef FormatTypeMapping
return table[t];
}
define typedef enum in class header:
typedef enum {
IngredientType_text = 0,
IngredientType_audio = 1,
IngredientType_video = 2,
IngredientType_image = 3
} IngredientType;
write a method like this in class:
+ (NSString*)typeStringForType:(IngredientType)_type {
NSString *key = [NSString stringWithFormat:@"IngredientType_%i", _type];
return NSLocalizedString(key, nil);
}
have the strings inside Localizable.strings file:
/* IngredientType_text */
"IngredientType_0" = "Text";
/* IngredientType_audio */
"IngredientType_1" = "Audio";
/* IngredientType_video */
"IngredientType_2" = "Video";
/* IngredientType_image */
"IngredientType_3" = "Image";
Another solution:
typedef enum BollettinoMavRavTypes {
AMZCartServiceOperationCreate,
AMZCartServiceOperationAdd,
AMZCartServiceOperationGet,
AMZCartServiceOperationModify
} AMZCartServiceOperation;
#define AMZCartServiceOperationValue(operation) [[[NSArray alloc] initWithObjects: @"CartCreate", @"CartAdd", @"CartGet", @"CartModify", nil] objectAtIndex: operation];
In your method you can use:
NSString *operationCheck = AMZCartServiceOperationValue(operation);
Combining @AdamRosenfield answer, @Christoph comment and another trick to handle plain C enums I suggest:
// In a header file
typedef enum {
JSON = 0, // explicitly indicate starting index
XML,
Atom,
RSS,
FormatTypeCount, // keep track of the enum size automatically
} FormatType;
extern NSString *const FormatTypeName[FormatTypeCount];
// In a source file
NSString *const FormatTypeName[FormatTypeCount] = {
[JSON] = @"JSON",
[XML] = @"XML",
[Atom] = @"Atom",
[RSS] = @"RSS",
};
// Usage
NSLog(@"%@", FormatTypeName[XML]);
In the worst case - like if you change the enum but forget to change the names array - it will return nil for this key.
Source: Stackoverflow.com