Where to store global constants in an iOS application?
Most of the models in my iOS app query a web server. I would like to have a configuration file storing the base URL of the server. It will look something like this:
// production
// static NSString* const baseUrl = "http://website.example/"
// testing
static NSString* const baseUrl = "http://192.168.0.123/"
By commenting out one line or the other, I can instantly change which server my models point to. My question is, what's the best practice for storing global constants in iOS? In Android programming, we have this built-in strings resource file. In any Activity (the equivalent of a UIViewController), we can retrieve those string constants with:
String string = this.getString(R.string.someConstant);
I was wondering if the iOS SDK has an analogous place to store c开发者_开发百科onstants. If not, what is the best practice in Objective-C to do so?
Well, you want the declaration local to the interfaces it relates to -- the app-wide constants file is not a good thing.
As well, it's preferable to simply declare an extern NSString* const
symbol, rather than use a #define
:
SomeFile.h
extern NSString* const MONAppsBaseUrl;
SomeFile.m
#import "SomeFile.h"
#ifdef DEBUG
NSString* const MONAppsBaseUrl = @"http://192.168.0.123/";
#else
NSString* const MONAppsBaseUrl = @"http://website.example/";
#endif
Apart from the omission of the C++ compatible Extern declaration, this is what you will generally see used in Apple's Obj-C frameworks.
If the constant needs to be visible to just one file or function, then static NSString* const baseUrl
in your *.m
is good.
You could also do a
#define kBaseURL @"http://192.168.0.123/"
in a "constants" header file, say constants.h
. Then do
#include "constants.h"
at the top of every file where you need this constant.
This way, you can switch between servers depending on compiler flags, as in:
#ifdef DEBUG
#define kBaseURL @"http://192.168.0.123/"
#else
#define kBaseURL @"http://myproductionserver.example/"
#endif
The way I define global constants:
AppConstants.h
extern NSString* const kAppBaseURL;
AppConstants.m
#import "AppConstants.h"
#ifdef DEBUG
NSString* const kAppBaseURL = @"http://192.168.0.123/";
#else
NSString* const kAppBaseURL = @"http://website.example/";
#endif
Then in your {$APP}-Prefix.pch file:
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "AppConstants.h"
#endif
If you experience any problems, first make sure that you have the Precompile Prefix Header option set to NO.
You can also concatenate string constants like this:
#define kBaseURL @"http://myServer.example"
#define kFullURL kBaseURL @"/api/request"
I do think that another way to do this is far simpler and you will just include it in files you need them included in, not ALL the files, like with the .pch prefix file:
#ifndef Constants_h
#define Constants_h
//Some constants
static int const ZERO = 0;
static int const ONE = 1;
static int const TWO = 2;
#endif /* Constants_h */
After that you include this header file in the header file that you want. You include it in header file for the specific class that you want it included in:
#include "Constants.h"
- I define global constant in YOURPROJECT-Prefix.pch file.
#define BASEURl @"http://myWebService.appspot.com/xyz/xx"
then anywhere in project to use BASEURL:
NSString *LOGIN_URL= [BASEURl stringByAppendingString:@"/users/login"];
Updated: In Xcode 6 you will not find default .pch file created in your project. So please use PCH File in Xcode 6 to insert .pch file in your project.
Updates: For SWIFT
- Create new Swift file [empty without class] say [AppGlobalMemebers]
& Right away declare / define member
Example:
var STATUS_BAR_GREEN : UIColor = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1) //
- If you want to define the app global member in any class file say Appdelegate or Singleton class or any, declare given member above class definition
Global declarations are interesting but, for me, what changed deeply my way to code was to have global instances of classes. It took me a couple of day's to really understand how to work with it so I quickly summarized it here
I use global instances of classes (1 or 2 per project, if needed), to regroup core data access, or some trades logics.
For instance if you want to have a central object handling all restaurant tables you create you object at startup and that is it. This object can handle database accesses OR handle it in memory if you don't need to save it. It's centralized, you show only useful interfaces ... !
It's a great help, object oriented and a good way to get all you stuff at the same place
A few lines of code :
@interface RestaurantManager : NSObject
+(id) sharedInstance;
-(void)registerForTable:(NSNumber *)tableId;
@end
and object implementation :
@implementation RestaurantManager
+ (id) sharedInstance {
static dispatch_once_t onceQueue;
dispatch_once(&onceQueue, ^{
sharedInstance = [[self alloc] init];
NSLog(@"*** Shared instance initialisation ***");
});
return sharedInstance;
}
-(void)registerForTable:(NSNumber *)tableId {
}
@end
for using it it's really simple :
[[RestaurantManager sharedInstance] registerForTable:[NsNumber numberWithInt:10]]
The accepted answer has 2 weaknesses.
First, as others pointed is usage of #define
which is harder to debug, use instead extern NSString* const kBaseUrl
structure.
Second, it defines a single file for constants. IMO, this is wrong because most of classes don't need access to those constants or to access all of them plus file can become bloated if all constants are declared there. A better solution would be to modularize constants at 3 different layers:
System layer:
SystemConstants.h
orAppConstants.h
which describes constants at global scope, which can be accessed by any class in the system. Declare here only those constants that must be accessed from different classes that are not related.Module/Sub-system layer:
ModuleNameConstants.h
, describes a set of constants which are typical for a set of related classes, inside of a module/sub-system.Class layer: Constants resides in the class and are used only by it.
Only 1,2 are related to the question.
An approach I've used before is to create a file Settings.plist
and load it into NSUserDefaults
upon launch using registerDefaults:
. You can then access its contents with the following:
// Assuming you've defined some constant kMySettingKey.
[[NSUserDefaults standardUserDefaults] objectForKey:kMySettingKey];
While I haven't done any Android development, it sounds as though this is analogous to the strings resource file you described. The only downside is that you can't use the preprocessor to swap between settings (e.g. in DEBUG
mode). I suppose you could load in a different file, though.
NSUserDefaults
documentation.
For a number you can use it like
#define MAX_HEIGHT 12.5
I'd use a configuration object that initializes from a plist
.
Why bother other classes with irrelevant external stuff?
I created eppz!settigns
soley for this reason. See article Advanced yet simple way to save to NSUserDefaults for incorporating default values from a plist
.
精彩评论