Previously in Objective-C, I would be able to do something like:
NSInteger count = 100;
NSString *text = [NSString stringWithFormat:#"count: %#", #(count)];
Objective-C had introduced the concept of autoboxing primitive literals so I didn't have to deal with %d, %ld, etc. It'll automatically convert the primitive literal into an NSObject which the formatter to parse as a %#.
In swift, is there something similar we can do?
let count: Int = 100
let text = String.init(format: "count of %#", count)
The above crashes because count is not a pointer to an object.
I know I can do inline parameter injection like:
let text = "count of \(count)"
But due to localization purposes, localizing the string format needs to happen before updating the parameters, which the code immediately above can't do.
Use Foundation types and bridge to NSNumber?
let count1: NSNumber = 100 // 100
let count2: NSNumber = 0.0 // 0
let count3: NSNumber = 2.73 // 2.73
let text = String(format: "count of %# %# %#", count1, count2, count3) // "count of 100 0 2.73"
That's really what "autoboxing" does, it wraps a primitive type in an Objective-C NSNumber object.
Doesn't quite answer the question, but I worked around the problem by wrapping my parameter with String(…), e.g.
let count: Int = 100
let text = String.init(format: "count of %#", String(count))
Update: an equivalent implementation to the above:
let count: Int = 100
let text = String.init(format: "count of %#", "\(count)")
Here's how I usually accomplish this: I just use a placeholder for the localization and swap it out after. So my strings files would look like this:
//English
"Could not read <name>" = "Could not read <name>."
//Spanish
"Could not read <name>" = "No puede leer <name>."
And my code would look like this:
//assume you have a fileName variable
var localizedString = NSLocalizedString("Error reading <name>", comment: "Error message when an object could not be read.")
localizedString = localizedString.replacingOccurrences(of: "<name>" with: fileName)
Instead of using an already-localized fileName, as I have in my example above, you could easily swap the placeholder for something like "\(count)". Just make sure that you localize the dropped-in value so that it displays nicely when put together with the text.
Related
I have in my objective-c application a number of constants that I need to have inputted from an external source using strings. The reason of course, is that constants are better to work with, but can't be passed external.
I have made this objective-c code to convert, and it works 100%, but a) it is ugly, and b) quite obscure. I suppose I could have converted to NSNumber and made an array, but that seems like a lot of code/processing (though maybe the right solution)
Can anyone provide a better solution?
NSArray *types = #[#"text_input",#"textbox",#"select",#"yesno",#"date",#"signature",#"label",#"SectionHeading"];
int indexes[10];
indexes[0] = FieldTypeTextInput;
indexes[1] = FieldTypeTextBox;
indexes[2] = FieldTypeSelect;
indexes[3] = FieldTypeYesNo;
indexes[4] = FieldTypeDate;
indexes[5] = FieldTypeSignature;
indexes[6] = FieldTypeLabel;
indexes[7] = FieldTypeSectionHeading;
for (int i=0;i<[types count];i++)
{
NSString *string_i = [types objectAtIndex:i];
if ([type_string isEqualToString:string_i])
I suggest to use an NSDictionary.
enum YourNiceTypes : NSInteger {FieldNotFound, FieldTypeTextInput, FieldTypeTextBox, ...};
NSDictionary *types = #{"text_input" : #(FieldTypeTextInput), ... };
enum YourNiceType type = [types[textInput] integerValue];
You used the trick to define wrong input with zero, which will be handled automatically correctly, as calling integerValue on a nil object will return 0.
I have converted an NSArray to NSString using below code
Suppose i have an array with some data in it.
NSString *sample=[array description];
NSLog(#"%#",sample);
which prints:
(
{
URL = "1a516af1a1c6020260a876231955e576202bbe03.jpg##37911944cc1ea8fd132ee9421a7b3af326afcc19.jpg";
userId = 0;
wallpaperId = 31;
},
{
URL = "a9356863fa43bc3439487198283321622f88e31f.jpg##f09c743ebdc26bb9f98655310a0529b65a472428.jpg";
userId = 0;
wallpaperId = 30;
}
)
It looks like array but it is actually a string.
Now I am wondering, how can I reconvert back to NSArray?
Help appreciated.
And please this not a duplicate question, I couldn't found the same anywhere on SO.
You cannot rely on the results of description as it is not a convert to string operator, but merely a debugging aid. There is nothing to stop it changing between O/S releases and there is no equivalent fromDescription method.
The conventional way of serializing an Objective-C collection to and from a string is to use JSON, so look at the NSJSONSerialization class.
the
NSString componentsseparatedbystring
method will return an array of components separated by a string
I'm currently working on a project where the user defines some parameters in a NSDictionnary, that I'm using to setup some objects.
For example, you can ask to create a Sound object with parameters param1=xxx, param2=yyy, gain=3.5 ... Then an Enemi object with parameters speed=10, active=YES, name=zzz ...
{
active = NO;
looping = YES;
soundList = "FINAL_PSS_imoverhere_all";
speed = 100.0;
}
I then instantiate my classes, and would like to set the ivars automatically from this dictionnary.
I've actually wrote some code to check that this parameter exists, but I'm having trouble in actually setting the parameter value, especially when the parameter is non object (float or bool).
Here's what I'm doing so far :
//aKey is the name of the ivar
for (NSString *aKey in [properties allKeys]){
//create the name of the setter function from the key (parameter -> setParameter)
NSString *setterName = [aKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[aKey substringToIndex:1] uppercaseString]];
setterName = [NSString stringWithFormat:#"set%#:",setterName];
SEL setterSelector = NSSelectorFromString(setterName);
//Check if the parameter exists
if ([pge_object respondsToSelector:setterSelector]){
//TODO : automatically set the parameter
}
else{
[[PSMessagesChecker sharedInstance]logMessage:[NSString stringWithFormat:#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]] inColor:#"red"];
NSLog(#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]);
}
}
}
As you can see, I don't know what to do once I've found that the parameter exists on the object. I tried to use "performSelector... withObject..., but my problem is that some of the parameters are non-objects (float or bool).
I also tried to get the class of the parameter, by using the setter, but it didn't help.
Did anyone manage to do something like that?
Jack Lawrence's comment is spot on.
What you are looking for is called Key Value Coding, or just KVC.
This fundamental part of Cocoa lets you get and set any instance variable using its name as a String and a new value.
It will automatically handle coercing Objects to primitive values, so you can use it for int and float properties too.
There is also support for validating values and handling unknown properties.
see the docs
your code, without validation, could be written
for( id eachKey in props ) {
[anOb setValue:props[eachKey] forKey:eachKey];
}
or just
[anOb setValuesForKeysWithDictionary:props];
as Jack said.
For the non-object parameters you have to put them into an object, for example NSNumber or NSValue. You can then add these objects into your dictionary.
For Example:
float f = 0.5;
NSNumber f_obj = [NSNumber numberWithFloat:f];
I'm very new to objective-c and I'm getting a basic error and unsure on how to fix it.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data; //sym.data = 0012044012482
[self phpPost:(int)sym.data];
break;
}
}
- (void)phpPost: (int)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode]; //problem line
labelScan.text = theValue;
//labelScan.text = #"Barcode scanned";
}
when i use #"%#" the text of the label is correct (0012044012482), but when i use #"%d" it isn't (random number every time i restart the app). I would like to use #"%#" (or something that works) but for some reason xCode is giving me an error. and I'm unsure on how to fix the error.
The error is:
Format specifies type 'id' but the argument has type 'int'
In the end I plan on having that code (sym.data) written to a MySQL database using the POST method.
You can't just convert it to an int by casting if it's an object (which it must be if the %# format specifier isn't causing a crash).
Assuming from the fact that you're assinging it directly to a label's text that it's an NSString, you should either change the parameter type of phpPost:
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
}
or extract the intValue before passing sym.data:
[self phpPost:[sym.data intValue]];
and then use the proper %d format specifier in phpPost:.
Your barcode isn't an int, it is an NSString. Instead of doing (int)sym.data, pass in [sym.data intValue]. That should correctly convert it to an integer.
The reason you get a random number is because you can't just cast a string object to a primitive data type :)
I don't know what type sym.data is, but it is likely a pointer to an object, and not the value itself. You cast that pointer to int, so when you are using %d you are effectively printing the memory location of the object. That is why it changes each time you run the program (Objective-C let's you do this without any warnings - something to watch out for).
To fix this, either extract the integer value you need from the sym.data object using it's properties; or pass the object as a pointer. For instance, you could try calling your method like this:
[self phpPost:sym.data];
And then change your method to be:
- (void)phpPost: (id)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode];
labelScan.text = theValue;
}
Ok, I did some thinking while I was at work today, and I figured out that an INT isn't going to work for me. if I make that object to an int, I would loss some data that is vital to what I'm doing. eg. object=001234 int=1234. I need the zeros. So, in the end, I'm keeping it an object (string) and just passing it into the function.
Here is my code after I got it working correctly.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data;
[self phpPost:sym.data];
break;
}
}
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
//labelScan.text = #"Barcode scanned"; //My custon label
}
Thanks, everyone for your responses. Your answer will not go unused. I'm sure I'll be needing this information here soon.
O, if you see that I did this wrong, or not the correct way, please make a comment and tell me .
I have tried using a variable as an input parameter to NSLocalizedString, but all I am getting back is the input parameter. What am I doing wrong? Is it possible to use a variable string value as an index for NSLocalized string?
For example, I have some strings that I want localized versions to be displayed. However, I would like to use a variable as a parameter to NSLocalizedString, instead of a constant string. Likewise, I would like to include formatting elements in the parameter for NSLocalizedString, so I would be able to retrieved a localized version of the string with the same formatting parameters. Can I do the following:
Case 1: Variable NSLocalizedstring:
NSString *varStr = #"Index1";
NSString *string1 = NSLocalizedString(varStr,#"");
Case 2: Formatted NSLocalizedString:
NSString *string1 = [NSString stringWithFormat:NSLocalizedString(#"This is an %#",#""),#"Apple"];
(Please note that the variable can contain anything, not just a fixed set of strings.)
Thanks!
If what you want is to return the localized version of "This is an Apple/Orange/whatever", you'd want:
NSString *localizedVersion = NSLocalizedString(([NSString stringWithFormat:#"This is an %#", #"Apple"]), nil);
(I.e., the nesting of NSLocalizedString() and [NSString stringWithFormat:] are reversed.)
If what you want is the format to be localized, but not the substituted-in value, do this:
NSString *finalString = [NSString stringWithFormat:NSLocalizedString(#"SomeFormat", nil), #"Apple"];
And in your Localizable.strings:
SomeFormat = "This is an %#";
I just want to add one very helpful definition which I use in many of my projects.
Inspired by androids possibility, I've added this function to my header prefix file:
#define NSLocalizedFormatString(fmt, ...) [NSString stringWithFormat:NSLocalizedString(fmt, nil), __VA_ARGS__]
This allows you to define a localized string like the following:
"ExampleScreenAuthorizationDescriptionLbl"= "I authorize the payment of %# to %#.";
and it can be used via:
self.labelAuthorizationText.text = NSLocalizedFormatString(#"ExampleScreenAuthorizationDescriptionLbl", self.formattedAmount, self.companyQualifier);
For swift :
let myString = String(format: NSLocalizedString("I authorize the payment of %d ", comment: ""), amount)
extension String {
public var localizedString: String {
return NSLocalizedString(self, comment: "")
}
public func localizedString(with arguments: [CVarArg]) -> String {
return String(format: localizedString, arguments: arguments)
}
}
Localizable.string:
"Alarm:Popup:DismissOperation:DeviceMessage" = "\"%#\" will send position updates on a regular basis again.";
"Global:Text:Ok" = "OK";
Usage:
let message = "Alarm:Popup:DismissOperation:DeviceMessage".localizedString(with: [name])
and
let title = "Global:Text:Ok".localizedString
It turns out that a missing target entry is to blame. Just checking that my current build target includes the Localizable.string file solved the problem!
If you have more than one variable in your localized string can you use this solution:
In Localizable.strings
"winpopup" = "#name# wins a #type# and get #points# points(s)";
And use stringByReplacingOccurrencesOfString to insert the values
NSString *string = NSLocalizedString(#"winpopup", nil); //"#name# wins a #type# and get #points# points(s)"
NSString *foo = [string stringByReplacingOccurrencesOfString:#"#name#" withString:gameLayer.turn];
NSString *fooo = [foo stringByReplacingOccurrencesOfString:#"#type#" withString:winMode];
NSString *msg = [fooo stringByReplacingOccurrencesOfString:#"#points#" withString:[NSString stringWithFormat:#"%i", pkt]];
NSLog(#"%#", msg);
Your ideas should work. But if you are getting back the input parameter, that means that the input parameter was not found as a key in your Localizable.strings file. Check the syntax and location of that file.
This works for me:
NSMutableString *testMessage = [NSMutableString stringWithString:NSLocalizedString(#"Some localized text", #"")];
testMessage = [NSMutableString stringWithString:[testMessage stringByAppendingString:someStringVariable]];