Typedef enum declaration: which is correct - objective-c

So I been used to use this format to declare a enum types:
typedef enum SortType {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
} SortType;
But I saw some people declare it this way
typedef enum {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
} SortType;
Both seems to work and no error. But I want to know which is correct.

I would recommend:
typedef NS_ENUM(NSInteger, SortType) {
SortTypeName,
SortTypeCompany,
SortTypeNone
};
as per the Apple Developer Guides and Sample Code: Adopting Modern Objective-C > Enumeration Macros

Between those two, there isn't a wrong form per se. That said, the current recommended way to declare enums in Objective-C is using the NS_ENUM macro:
typedef NS_ENUM(NSInteger, SortType) {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
};
From Apple's Adopting Modern Objective-C guide:
The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.
Use the NS_ENUM macro to define enumerations, a set of values that are mutually exclusive:
The NS_ENUM macro helps define both the name and type of the enumeration, in this case named UITableViewCellStyle of type NSInteger. The type for enumerations should be NSInteger.

Related

Accessing NS_ENUM of objective-C in Swift

I have this NS_ENUM called ObserveType having two properties called Observe and ObserveAll. I can access the ObserveAll property as you can see from the picture, but I can't access Observe.
The NS_ENUM is in a header file of objective-C.
I know that changing Observe to ObserveX or ObserveXYZ will work.
But how do I access Observe without changing the name of the Observe?
Notice that I have to access the Observe on Swift.
In addition to answers above, I'd like to point out that you can give your Objective-C NS_ENUM a Swift name with NS_SWIFT_NAME macro:
typedef NS_ENUM(NSUInteger, XYZAwesomeEnum) {
XYZAwesomeEnumA,
XYZAwesomeEnumB,
XYZAwesomeEnumC,
} NS_SWIFT_NAME(AwesomeEnum);
Use it later in Swift:
AwesomeEnum.a
Apple Developer: Renaming Objective-C APIs for Swift
From Language Guide - Interoperability - Interacting with C APIs:
"The prefixes to C enumeration case names are removed when they are imported into Swift, whether they’re defined in system frameworks or
in custom code."
This means your first case in the ObserveType enum have no name after being imported to Swift (which I'm somewhat surprised doesn't yield a compile error). If we were to print out the conversion, it would look something like (conceptually)
typedef NS_ENUM(NSInteger, ObserveType) {
Observe = 0
ObserveAll = 1
};
// imported like ...
enum ObserveType: Int {
case _ = 0 // obviously illegal if applied directly in Swift
case All
}
You could try to access the nameless case by using its rawValue (0), but I would recommend updating the name of the first case in your Obj-C enum, if possible.
if let empty = ObserveType(rawValue: 0) {
print(empty) // prints ""?
}
dfri's answer is spot on, and to extend on his answer, it is probably best to name the enum and its cases following the widely adopted conventions. In the following code, I am making certain guesses on your intention.
typedef NS_ENUM(NSUInteger, XYZObserveType) {
XYZObserveOne,
XYZObserveAll
}
Another tip that I find useful when working with projects contain both Swift and Objective-C code is Generated Interface in the source editor.
When you are viewing an Objective-C class, selecting this option will show you the generated Swift header, which can be helpful in many occasions.

typedef NS_ENUM vs typedef enum

On the Adopting Modern Objective-C guide, Apple recommends using the NS_ENUM macro instead of enum. I've also read an explanation from NSHipster about NS_ENUM and NS_OPTIONS.
Maybe I've missed something but I don't quite understand what is the difference between the following two snippets and if any why is NS_ENUM the recommended way to go (except maybe, for backwards compatibility with older compilers)
// typedef enum
typedef enum {
SizeWidth,
SizeHeight
}Size;
// typedef NS_ENUM
typedef NS_ENUM(NSInteger, Size) {
SizeWidth,
SizeHeight
};
First, NS_ENUM uses a new feature of the C language where you can specify the underlying type for an enum. In this case, the underlying type for the enum is NSInteger (in plain C it would be whatever the compiler decides, char, short, or even a 24 bit integer if the compiler feels like it).
Second, the compiler specifically recognises the NS_ENUM macro, so it knows that you have an enum with values that shouldn't be combined like flags, the debugger knows what's going on, and the enum can be translated to Swift automatically.
NS_ENUM allows you to define a type. This means that the compiler can check if you're assigning the enum to a different variable like so:
//OK in both cases
NSInteger integer = SizeWidth;
//OK only with typedef
BOOL value = SizeHeight;
NS_ENUM also provides checks in switch statements that you've covered all possible values:
//Will generate warning if using NS_ENUM
switch(sizeVariable) {
case SizeWidth:
//Do something
}

NS_ENUM vs enum

Objective C provides several ways to declare an enumeration. It could be declared via typedef enum or NS_ENUM. NS_ENUM macro takes type name as a parameter, and I do not completely understand its meaning. I didn't find description of NS_ENUM macro in official Apple documentation. What's a difference between using enum and NS_ENUM? And an other question if it's possible to use any other type in NS_ENUM instead NSInteger and its relative integer types?
NSHipster provided a very nice post that explains this thoroughly:
http://nshipster.com/ns_enum-ns_options/
To quote the bottom line:
This approach combines the best of all of the aforementioned approaches (enum, typedef enum), and even provides hints to the compiler for type-checking and switch statement completeness.
The main difference is that typedef NS_ENUM translates to a Swift enum properly, whereas typedef enum doesn't.
Here is the link to the official Apple's documentation:
https://developer.apple.com/library/content/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW6
From the subsection titled "Enumeration Macros":
The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.
Use the NS_ENUM macro to define enumerations, a set of values that are mutually exclusive:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
The NS_ENUM macro helps define both the name and type of the enumeration, in this case named UITableViewCellStyle of type NSInteger. The type for enumerations should be NSInteger.

Can the underlying type of an Objective-C enum be specified?

I was surprised to discover that Xcode does not issue an error for the following code, which is invalid in C:
typedef enum : NSUInteger {
FLAG_A = 1,
FLAG_B = 2,
FLAG_C = 4
} MyFlags;
This appears to make NSUInteger the underlying type of the MyFlags enum, very similar to C#'s syntax for setting the underlying type of an enum.
Does this syntax do what it appears, or does it mean something else?
It is a feature borrowed from C++11 used to ensure a specific size of the enum. It allows to use enums in the public API of frameworks without having to fear that the ABI will eventually break.
Clang implements this feature and allows its use in Objective-C as a language extension.
This will work fine in objective c. you can set any value for these params in an enum.

Is it required to typedef a primitive type to an enum?

I was looking through the NSString header file to see how Apple writes their enumerations and came across this piece of code:
enum {
NSStringEncodingConversionAllowLossy = 1,
NSStringEncodingConversionExternalRepresentation = 2
};
typedef NSUInteger NSStringEncodingConversionOptions;
This leaves me with a couple questions.
Why have they used anonymous enumerations? Is this method advantageous?
Is the typedef NSUInteger NSStringEncodingConversionOptions; line a good idea to include normally, or is it only used here because they have declared an anonymous enumeration?
That strange-looking definition is there to clearly define the bit-width and the signed-ness of an enum in a code both in 64-bit and 32-bit environment. It's detailed in this Apple document, but let me write down the summary here.
Apple in the past used the standard typedef enums before, as in
typedef enum { .... } NSEnumTypeName;
before 64bit-32bit universal binaries were (re)introduced. (I used "re" because the FAT binaries have been there since the NeXTStep days. Anyway.)
However, this makes the bit-width and the signed-ness of the typedef'd type NSEnumTypeName to be implementation-defined as specified in the Official Standard, see 6.7.2.2.4.
That makes it even trickier to write a code which can be compiled with various compilers and with various bit-width.
So Apple switched from the standard enum to the anonymous enum with a corresponding typedef to a specific signed/unsigned integer type.