In iOS how to use NS_ENUM? - ios

I know that such questions are asked before. I looked on the net find examples but I can not figure out how to use NS_ENUM in my code.
I have a list of error codes. To manage them I want to use NS_ENUM. I create a class which contains all my public const for the application. I want to define my enum here and use it everywhere in the app.
My code in .h file:
typedef NS_ENUM(NSInteger, wsErrEnum);
In my .m file:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
I thought that I can check in the following manner:
if(result.ErrCode == wsErrEnum.ERR_NONE) {
// do stuff ...
}
, but ERR_NONE is not visible as property.
So my question is how to define and use NS_ENUM in this case.
EDIT:
Using
if(result.ErrCode == ERR_NONE) {
NSLog(#"It is OK!");
}
give me error that ERR_NONE is undefined.
EDIT 2:
I understand the problem. I declare NS_ENUM in my .h file (as some comments and answers suggest) and the error disappears. I try this before but in that case I did not use enum properly - I use is as wsErrEnum.ERR_NONE. 10x for help.
P.S. My class is #imported in the .m file where I try to make this comparison.

The only problem here is that you've declared the enum's values in your .m file.
You should declare the entire enum (values included) in your .h file. By declaring the enum type wsErrNum in your .h file and the values in your .m file, you've hidden the values from all other classes.
So, in your .h file:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
Nothing needs to go in your .m file to declare this enum.

In Swift :
if result.ErrCode == wsErrEnum.ERR_NONE {
// do stuff ...
}
In Objective-C :
if(result.ErrCode == ERR_NONE) {
// do stuff ...
}
The real issue is that you have split up the declaration of wsErrEnum, remove it from the .m file and place the full delcaration in the .h:
typedef NS_ENUM(NSInteger, wsErrEnum) {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 1
};
But better is something like:
typedef NS_ENUM(NSInteger, wsErrEnum) {
wsErrEnumName = 0,
wsErrEnumTimeOut = 1
};
Just a small remark, properties like your ErrCode should not started with a capital letter.

Normally, I use Enumerations in objective-c as typedef enum. Doing like this, you don't need an implementation file (.m) and the code looks like this:
// Enumerations.h
#interface Enumerations
typedef enum:int {
ERR_NONE,
ERR_HOST_TIMEOUT
} wsErrEnum;
#end
By default, the compiler values the enumerations from 0 to N, in order of which they are declared. In the example above, ERR_NONE is 0 and ERR_HOST_TIMEOUT is 1.
But you could also value the enumerations the way you preffer:
typedef enum:int {
ERR_NONE = 0,
ERR_HOST_TIMEOUT = 504
} wsErrEnum;
Then, you could work with switch and it would consider your enumeration, so it throws a warning in case you don't implement one possible result, for example:
switch(result.ErrCode) {
case ERR_NONE:
//implementation
break;
}
The code above will warn you that you don't have a case implemented for ERR_HOST_TIMEOUT.
You can also import your Enumerations.h in the file YourProjectName-Prefix.phc so it can be used in your hole project.
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Enumerations.h" //Add this line so you don't have to import your .h file every time you use it
#endif

Related

Swift does not import typedef from Objective-C Header

It seems that Swift does not recognize a typedef in a Objective-C-Header as I get following error:
Could not find a user-defined conversion from type 'MMDrawerControllerDrawerVisualStateBlock!' to type '(MMDrawerController!, MMDrawerSide, CGFloat) -> Void'
I use the MMDrawerController which is written in Objective-C, my own code though is in Swift.
The typedef looks like this:
typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible);
Here are more code snippets for clarity:
AppDelegate.swift
func initDrawerController() {
drawerController = MMDrawerController(centerViewController: centerController, leftDrawerViewController: leftDrawerController, rightDrawerViewController: rightDrawerController)
drawerController?.setDrawerVisualStateBlock(MMDrawerVisualState.parallaxVisualStateBlockWithParallaxFactor(2.0))
}
MMDrawerController.h
typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible);
#interface MMDrawerController : UIViewController
-(void)setDrawerVisualStateBlock:(void(^)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible))drawerVisualStateBlock;
#end
MMDrawerVisualState.h
#interface MMDrawerVisualState : NSObject
+(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor;
#end
Module-Bridging-Header.h
#import "MMDrawerController.h"
#import "MMDrawerVisualState.h"
When building this, I get an error in my AppDelegate for the Expression with setDrawerVisualStateBlock, although there is a typedef in the MMDrawerController.h:
Is this a bug (because on Objective-C, it works fine)? Or is there anyone who knows/has an idea how to deal with it?
Help is much appreciated, thanks!
Objective-C typedef declaration (in MMDrawerController.h for example) :
typedef NS_ENUM(NSInteger, MMDrawerOpenCenterInteractionMode) {
MMDrawerOpenCenterInteractionModeNone,
MMDrawerOpenCenterInteractionModeFull,
MMDrawerOpenCenterInteractionModeNavigationBarOnly,
};
In Swift, it’s imported like this :
enum MMDrawerOpenCenterInteractionMode: NSInteger {
case None
case Full
case Only
}
So you can use this syntax in Swift :
var mmDrawerViewController:MMDrawerController! = ....... ;
mmDrawerViewController.centerHiddenInteractionMode = MMDrawerOpenCenterInteractionMode.Full;
For use mmDrawerViewController.setDrawerVisualStateBlock()
try
mmDrawerViewController.setDrawerVisualStateBlock{ (drawerController:MMDrawerController!, drawerSlide:MMDrawerSide!, cgFloat:CGFloat!) -> Void in
println("setDrawerVisualStateBlock");
...
}
The reason is that you access MMDrawerSide drawerSide but are probably not importing the file that declares MMDrawerSide in your bridging header.
If an Objective-C method declaration accesses a class which is not imported in your bridging header, Swift doesn't import nor recognize that method.
So, if you have a typedef in Objective-C like this:
typedef void (^someBlock)(MMSomeClass *someInstance);
Then you must make sure that MMSomeClass is imported in your bridging header.

Convert to ARC -- resolve forward references

I've inherited Objective-C code that I'm moving to ARC. I've a number of .h files that declare a protocol and an interface that reference one another -- here's the basic pattern in the .h file
#class BasicService;
#class BasicServiceObserver;
#protocol BasicServiceObserver
-(void)UPnPEvent:(BasicService*)sender events:(NSDictionary*)events;
#end
#interface BasicService : NSObject <Events_Observer> {
<<var declarations here>>
}
-(NSUInteger)addObserver:(BasicServiceObserver*)obs;
-(NSUInteger)removeObserver:(BasicServiceObserver*)obs;
-(BOOL)isObserver:(BasicServiceObserver*)obs;
#end
and in the .m file, the BasicServiceObserver ref is flagged as a forward reference. The .m file includes the above .h file.
//Events_Observer
-(void)someEvent:(NSDictionary*)events{
BasicServiceObserver *obs = nil;
[mMutex lock];
NSEnumerator *listeners = [mObservers objectEnumerator];
while(obs = [listeners nextObject]){
[obs someEvent:self events:events]; // <-- Receiver type 'BasicServiceObserver' for instance message is a forward declaration
}
[mMutex unlock];
}
Driving me nuts. I've tried to separate the the protocol into its own .h file and import it (and remove the #class declaration). No joy there.
Any ideas on how to resolve this?
thanks!
Since BasicServiceObserver is a protocol, not a class, it should not be forward-declared in this situation at all. Declaring it as a #class misleads the compiler into thinking that there would be an #interface BasicServiceObserver somewhere when there is no such interface.
Here is how you should get this to a state where it compiles:
#class BasicService;
#protocol BasicServiceObserver
-(void)UPnPEvent:(BasicService*)sender events:(NSDictionary*)events;
#end
#interface BasicService : NSObject <Events_Observer> {
<<var declarations here>>
}
-(NSUInteger)addObserver:(id<BasicServiceObserver>)obs;
-(NSUInteger)removeObserver:(id<BasicServiceObserver>)obs;
-(BOOL)isObserver:(id<BasicServiceObserver>)obs;
#end
Similarly, in .m file
//Events_Observer
-(void)someEvent:(NSDictionary*)events{
id<BasicServiceObserver> obs = nil;
[mMutex lock];
NSEnumerator *listeners = [mObservers objectEnumerator];
while(obs = [listeners nextObject]){
[obs someEvent:self events:events]; // <-- Receiver type 'BasicServiceObserver' for instance message is a forward declaration
}
[mMutex unlock];
}
Note: Since you are cleaning up the files anyway, consider moving the variables from the <<var declarations here>> section into a class extension in the .m file. This may help you reduce the number of headers included in the .h file, and to avoid propagating through your headers the unnecessary dependencies of the implementation
As others have pointed out, a class and a protocol are not the same thing.
Sometimes you do want to define both, e.g. the system UITableView class and the UITableViewDelegateProtocol protocol.
If you are going to have both a protocol and a class, I suggest adding the word protocol to the name of the protocol:
#protocol BasicServiceObserverProtocol
-(void)UPnPEvent:(BasicService*)sender events:(NSDictionary*)events;
#end
However, it's probably better to not create a BasicServiceObserver class. Instead, make all pointers to objects to conform to that protocol be
BasicServiceObserverProtocol, as #dasblinkenlight says in his post.

CocoaLumberjack with Swift - Calling preprocessor macros

I started to build an IOS app with the new programming language Swift. I managed to use CocoaPods and was able to successfully create the DDTTYLogger with my CustomLoggerFormatter (Objective-C) in my AppDelegate.swift and append it to the loggers.
var customLoggerFormatter = CustomLoggerFormatter()
var consoleLogger: DDTTYLogger = DDTTYLogger.sharedInstance()
consoleLogger.setLogFormatter(customLoggerFormatter)
DDLog.addLogger(consoleLogger)
But the problem is, that the CocoaLumberjack Library is using preprocessor macros for the logger methods like DDLogVerbose(#"..")
Which is defined in the DDLog.h:
#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
Is there any workaround to make preprocessor defines work in Swift? Or did anyone try something similar with more success?
Okay, I just found a solution. Writing an Objective-C Wrapper class calling the preprocessors and offering methods to call it.
Hopefully this will help other people facing the same issues.
I first created a header file:
#interface DDLogWrapper : NSObject
+ (void) logVerbose:(NSString *)message;
+ (void) logError:(NSString *)message;
+ (void) logInfo:(NSString *)message;
#end
With the corresponding implementation:
#import <Foundation/Foundation.h>
#import "DDLogWrapper.h"
// Logging Framework Lumberjack
#import "DDLog.h"
#import "DDASLLogger.h"
#import "DDTTYLogger.h"
// Definition of the current log level
#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif
#implementation DDLogWrapper
+ (void) logVerbose:(NSString *)message {
DDLogVerbose(message);
}
+ (void) logError:(NSString *)message {
DDLogError(message);
}
+ (void) logInfo:(NSString *)message {
DDLogInfo(message);
}
#end
Important is to add the DDLogWrapper.h File to the ProjectName-Bridging-Header.h file and then you are able to instantiate in Swift the DDLogWrapper and call the methods logVerbose, logError, logInfo..
With the following code I was able to make a log statement:
DDLogWrapper.logVerbose("TEST");
I created a Swift wrapper for CocoaLumberjack that encapsulates everything nicely.
DDLog.addLogger(DDTTYLogger.sharedInstance())
DDLog.logLevel = .Info
logInfo("Info")
logWarn("Warn")
logDebug("Debug")
logError("Error")
As of 2.0.0beta4, CocoaLumberJack includes a CocoaLumberJack.swift file that makes its integration with Swift projects really easily.
They use a global var defaultDebugLevel to set the DDLogLevel, and you can swift basic precompile macros to customize it to your needs.
#if DEBUG
defaultDebugLevel = DDLogLevel.All
#else
defaultDebugLevel = DDLogLevel.Warning
#endif
DDLog.addLogger(DDTTYLogger.sharedInstance())
DDLogDebug("Debug")
DDLogInfo("Info")
DDLogWarn("Warning")
DDLogVerbose("Verbose")
DDLogError("Error")

Linker Command Failed with exit code 1: duplicate symbol - no .m file

I've seen this issue in other posts but in my case I am using a sphere.h file that contains the data of vertices of a sphere, that is, a 3D model for my game project. I import my sphere.h file into my objective-C Class as follows:
#import "SceneEnergyGlobe.h"
#import "sphere.h"
#interface SceneEnergyGlobe() {
}
#property (strong, nonatomic) GLKTextureInfo *textureInfo0;
#end
.....
When I compile my project, I receive this compile error. How I can fix this?
Here is the contents of sphere.h:
#ifndef SPHERE_HEADER
#define SPHERE_HEADER
unsigned int sphereNumVerts = 2280;
float sphereVerts [] = {
0.0743889747124915, -0.49384436095363, -0.0241703260695731,
0.190555012144643, -0.979722062440628, -0.0619150039460291,
0.000000, 0.95,
0.0632787269334132, -0.49384436095363, -0.0459747512867777,
0.162096012330563, -0.979722074526971, -0.11776900895863,
0.050000, 0.95,
0.125000004921036, -0.475528075643002, -0.0908176095396332,
0.269869905435848, -0.942722669663907, -0.196071931295133,
.....
For every implementation file (.m) that includes sphere.h a new copy of the vertices array will be created. When these implementation files are linked together you get your duplicate symbol error.
The best approach is to change this in sphere.h:
#define sphereNumVerts 2280
extern float sphereVerts[sphereNumVerts];
And add an implementation file (sphere.m) that contains the definition of sphereVerts:
#import "sphere.h"
float sphereVerts[sphereNumVerts] = {
0.0743889747124915, -0.49384436095363, -0.0241703260695731,
...
};
BTW, those look like big numbers for float; are you sure you don't want double?
(EDIT: My previous suggestion was wrong; you'll get duplicate symbols using const in a header file too. I have changed this to a #define).
It is a very very very bad idea to create and initialize variables in .h files!!!

Why am I getting a type conflict warning on my enum in Objective-C?

Here is my Constants.h:
#import Foundation;
typedef NS_ENUM(NSUInteger, BarcodeType) {
kNormalBarcode,
kNoBarcode,
kGenericBarcode,
kInvalidBarcode,
kComicBarcode
};
#interface Constants : NSObject
#end
And here is a function that uses it:
.h:
#interface Helper : NSObject
- (BarcodeType)barcodeType:(NSString *)barcode;
.m:
#import "Constants.h"
...
- (BarcodeType)barcodeType:(NSString *)barcode
{
return kInvalidBarcode;
}
Why am I getting this warning on my function, and what do I need to change to fix it?
Conflicting return type in implementation of 'getBarcodeType:': 'id' vs 'BarcodeType' (aka 'enum BarcodeType')
This code seemed to work fine with older versions of Xcode.
Thanks!
Check your .h file. My guess is your declaration of the method uses id, which conflicts with your definition in the .m file that returns type BarcodeType. NSEnum values aren't objects, so id isn't valid. You'll want to correct the declaration so the return type matches the implementation.

Resources