NSLocalized String in Objective C - objective-c

In swift to create a localized string use `NSLocalizedString(key: , value: , comment:)
But in the older objective C https://developer.apple.com/documentation/foundation/nslocalizedstring
There only appears to be key and comment and key appears to act as both the key and the value in the xliff file.
My question is how do I give a key and value to a nslocalizedstring in objective C?

matt has the correct answer; however, to spell it out directly:
In Swift:
NSLocalizedString(key, value: value, comment: comment)
In Objective C:
NSLocalizedStringWithDefaultValue(key, nil, NSBundle.mainBundle, value, comment)
The Objective C version lets you look under the hood to see how it's done. Here is the code for the NSLocalizedString* macros in NSBundle.h.
#define NSLocalizedString(key, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:#"" table:nil]
#define NSLocalizedStringFromTable(key, tbl, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:#"" table:(tbl)]
#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
[bundle localizedStringForKey:(key) value:#"" table:(tbl)]
#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
[bundle localizedStringForKey:(key) value:(val) table:(tbl)]

You’re looking at a reduced form of the macro. The full form is called saying
NSString * NSLocalizedStringWithDefaultValue(
NSString *key,
NSString *tableName,
NSBundle *bundle,
NSString *value,
NSString *comment
)
See https://developer.apple.com/documentation/foundation/nslocalizedstringwithdefaultvalue
Just set the stuff you don’t care about at nil.

Related

Objective-C macro to detect if variable is primitive

I'm looking a macro to detect if a variable is an object or a primitive in Objective-C.
In this context, I know the parameter must be a variable and will never be an expression.
Here is the best thing I've come up with:
#define IS_OBJECT(x) ( #encode(__typeof__(x))[0] == '#' )
#define IS_PRIMITIVE(x) ( !IS_OBJECT(x) )
Usage:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
There must be a better way.
Update
Considering #ChrisDevereux comment, I updated the IS_OBJECT macro.
#define IS_OBJECT(x) ( strchr("##", #encode(__typeof__(x))[0]) != NULL )
It now passes:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
Class classTest = [NSString class];
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
STAssertTrue(IS_OBJECT(classTest), #"IS_OBJECT(classTest) must be YES");
I still don't like this answer, and hope there is something slicker. Is there something in the runtime library which does this?
Here's another way using C11's generic selection mechanism. _Generic is standard (modern) C and supported in clang for a while.
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
It feels a bit less runtime-ish to me so I prefer it over the #encode way. But to be honest I just used it for this answer because I love the power _Generic gives to macros and think more people should start using it. If you don't know it you should read Robert Gamble's article linked above.

__VA_ARGS__ Macro expansion

I'm trying to get my macro to work like NSLog() which accepts variable arguments. Code below causes parse issues.
What is the correct way to define this?
#define TF_CHECKPOINT(f, ...) \
do { \
NSString *s = [[NSString alloc] initWithFormat:f arguments:__VA_ARGS__] autorelease]; \
[TestFlight passCheckpoint:[NSString stringWithFormat:#"%#: %#", [self class], s]]; \
} while (0)
You forgot the opening bracket for the autorelease message.
Moreover -[NSString initWithFormat:arguments:] expects a va_list argument, whereas __VA_ARGS__ is replaced by all the passed arguments. Here, you need to use -[NSString initWithFormat:] or +[NSString stringWithFormat:].
Finally, you may prefix __VA_ARGS__ with ##. By doing so, the preceding comma is deleted when there is no argument.
Try this:
#define TF_CHECKPOINT(f, ...) \
do { \
NSString *s = [NSString stringWithFormat:(f), ##__VA_ARGS__]; \
[TestFlight passCheckpoint:[NSString stringWithFormat:#"%#: %#", [self class], s]]; \
} while (0)

Are strings defined as constants not NSStrings?

I have a constant defined as:
#define BEGIN_IMPORT_STRING #"Importing Hands!";
But I get an error when I try to concat with:
NSString *updateStr = [NSString stringWithFormat:#"%#%#", BEGIN_IMPORT_STRING, #" - Reading "];
This doesn't happen if I replace it with a string literal
NSString *updateStr = [NSString stringWithFormat:#"%#%#", #"foo", #" - Reading "];
Or a local string
NSString *temp = #"foo";
NSString *updateStr = [NSString stringWithFormat:#"%#%#", temp, #" - Reading "];
You need to remove the semicolon from your #define:
#define BEGIN_IMPORT_STRING #"Importing Hands!"
To the compiler, the resulting line looks like this:
NSString *updateStr = [NSString stringWithFormat:#"Importing Hands!";, #" - Reading "];
Replace
#define BEGIN_IMPORT_STRING #"Importing Hands!";
with
#define BEGIN_IMPORT_STRING #"Importing Hands!"
This is because compiler in your case replaces all occurrences of BEGIN_IMPORT_STRING with #"Importing Hands!";
Aside from the accepted answer (remove semicolon), note that:
#"Foo" is an NSString. You can even send it a message.
#define FOO #"Foo" is a preprocessor macro, not a constant. It's a typing shortcut.
Though macros aren't an uncommon way to avoid retyping the same string, they're an unfortunate holdover. Essentially, they're playing games that aren't necessary anymore.
For repeated strings, I prefer:
static NSString *const Foo = #"Foo;
The const portion of this definition ensures that the pointer is locked down, so that Foo can't be made to point to a different object.
The static portion restricts the scope to the file. If you want to access it from other files, remove the static and add the following declaration to your header file:
extern NSString *const Foo;
Should you be using
NSLocalizedString(#"Importing Hands!", #"Message shown when importing of hands starts");
?
I put it as an answer because this looks like something you would not want to have to go and redo through all your code.

nsstring in objective c

i am a beginer in objective c.i found the following line in code and is not able to understand what it does it do, as storeselect has not been used anywhere in the code.
NSString *storeSelect=#"";
Objective-C builds on C language. In C, quotes are placed around string literals, i.e. "hello". To distinguish NSString and C strings (char pointers, char *), Objective-C uses # in front of strings, so #"" is simply empty NSString. If there was no #, it would be empty C string, e.g. char *myString = "hello world";.
storeSelect is the name of a variable whose type is NSString *, with the value assigned to #""
It's just assigning an empty string to a variable named storeSelect. The #"" is for constant strings.
NSString *storeSelect=#"Hello World";
is a shortcut of -
NSString *str = [NSString stringWithCString:"Hello World"];
as "stringWithCString" is convenience method it will be automatically adds autoreleased.

Creating object named after the value of an NSString - is it possible?

Is it possible to create an object named after an NSString's value? If so, how?
Try this:
Class theClass = NSClassFromString(someString);
id object = [[theClass alloc] init];
If you mean that the string specifies the class name, then yes it's easy to do this using the NSClassFromString function to lookup the appropriate class "factory" object:
NSString* myClassName = #"NSNumber";
id myNewObject = [[NSClassFromString(myClassName) alloc] init];
// myNewObject is an NSNumber...
The example is contrived, but you get the idea.
Yes:
//In your header
extern NSString *FrobnitzerCalibrationHigh;
extern NSString *FrobnitzerCalibrationMedium;
extern NSString *FrobnitzerCalibrationLow;
//In your implementation
NSString *FrobnitzerCalibrationHigh = #"FrobnitzerCalibrationHigh";
NSString *FrobnitzerCalibrationMedium = #"FrobnitzerCalibrationMedium";
NSString *FrobnitzerCalibrationLow = #"FrobnitzerCalibrationLow";
You can make a couple of macros and put them in your prefix header:
//Semicolons intentionally omitted (see below)
#define DECLARE_STRING_CONSTANT(name) extern NSString *name
#define DEFINE_STRING_CONSTANT(name) NSString *name = ##name
Then use them in your class headers and implementations:
//In your header
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationHigh);
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationMedium);
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationLow);
//In your implementation
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationHigh);
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationMedium);
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationLow);
(The macros omit the semicolons because their usages will supply them. If the macros had semicolons as well, the expansion would be extern NSString *FrobnitzerCalibrationHigh;;—harmless in this case, but it would bug me if I did this, largely because it's not harmless in some other cases.)