IF statement issue in IOS using NSString - ios

My if statement won't work. active returns 1 but will not work in the IF statement
JSONDecoder *jsonKitDecoder = [JSONDecoder decoder];
NSDictionary *dict = [jsonKitDecoder objectWithData:jsonData];
NSString *userid = [dict valueForKeyPath:#"users.user_id"];
NSString *active = [dict valueForKeyPath:#"users.active"];
NSLog(#"%#",userid); // 2013-06-20 03:03:21.864 test[81783:c07] (74)
NSLog(#"%#",active); // 2013-06-20 03:03:21.864 test[81783:c07] (1)
if ([active isEqualToString:#"1"]){
// Do something
}
I can't seem to get this IF to work. Do I need to change the NSString to a int?

For starters, use a modern style for retrieving values from dictionaries, rather than valueForKeyPath:.
NSDictionary* users = dict[#"users"];
id active = users[#"active"];
Once you're using a modern style, my guess is that the active value is actually an NSNumber representing a boolean value. So your if block would read:
if([active isKindOfClass:NSNumber] && [active boolValue]) {
//active is an NSNumber, and the user is active
}

The syntax of your if statement is just fine. I would try the alternate method for retrieving values from a dictionary as mentioned above.
NSString *active = #"1";
if ([active isEqualToString:#"1"])
{
// Do something
NSLog(#"It works!");
}

More than likely the "users.active" object being returned from that NSDictionary-ized JSON stream is a "BOOL" or a "NSInteger" as the payload of a NSNumber object and it's not a NSString object.
Try using:
NSNumber * activeNumber = [dict valueForKeyPath: #"users.active"];
and see if "if ([activeNumber boolValue] == YES)" works better for you.

Related

Comaparing NSString in Objective C

Any one please help me to understand the String comparison technique in Objective-C
NSString *strNew1 = #"AA";
NSString *strNew2 = #"AA";
So to compare both the strings we could use,
Method 1. if (strNew1 == strNew2) {
NSLog(#"Equal");
}
or
Method 2: if ([strNew1 isEqualToString:strNew2]) {
NSLog(#"Equal");
}
In this condition both of them are success. But am aware that method 1 will get failed at certain other condition. And also I have tried the below conditions(All are success).
NSString *strNew = #"AA";
NSString *strNew1 = #"AA";
NSString *strNew11 = [[NSString alloc] initWithString:strNew1];
NSString *strNew3 = strNew;
NSArray *arr = #[#"AA"];
NSString *strNew4 = [arr objectAtIndex:0];
NSString *strNew5 = [arr objectAtIndex:0];
_test = strNew5;
_test1 = #"AA";
if ([strNew isEqualToString:strNew1]) {
NSLog(#"Equal");
}
if (strNew == strNew3) {
NSLog(#"Equal1");
}
if (strNew == [arr objectAtIndex:0]){
NSLog(#"Equal2");
}
if (strNew == strNew4){
NSLog(#"Equal3");
}
if (strNew5 == strNew4){
NSLog(#"Equal4");
}
if (strNew4 == [arr objectAtIndex:0]){
NSLog(#"Equal5");
}
if (strNew11 == [arr objectAtIndex:0]){
NSLog(#"Equal11");
}
if (self.test == strNew4){
NSLog(#"Equal3");
}
if (self.test == self.test1){
NSLog(#"Equal3");
}
TEST *test = [TEST new]; // Tried with a class with NSString property with value "AA" . (test.strTest value is #"AA")
if (strNew == test.strTest) {
NSLog(#"Equal"); //success
}
I knew most of them are redundant. Am not able to understand the basics behind this. Please anyone give clear explanation on the concept behind this. Thanks.
In the cases you defined the strings created are internally treated as string literals. The runtime will not allocate different memory space to such strings.
Essentially all the strings that contain the same string literal ("AA" in your case) will point to the same memory location. This is done as a part of memory optimization by Apple.
When you change the value of any string (say to "AB") a new address will be allocated to that NSString object and then == will fail.
You need to use below instance method of NSString class.
- (BOOL)isEqualToString:(NSString *)aString;
So, In your case simply follow below:
if ([strNew isEqualToString strNew4]){
NSLog(#"Equal3");
}
By doing (strNew == strNew4),
You are only comparing the addresses of the objects.
The first way compares pointers, while the second way compares objects.
That is, the first way compares if the pointers have the same value. In this case it is likely that they don't, in the second case the objects will be compared. Since they are initialized the same way they could be equal. (Note, it appears that with the UIButton's implementation of isEqual: the result is always false.)
In most cases using == is not what you want. However, what is appropriate depends on your objective.
if (strNew1 == strNew2) //This compared your pointers
{
}
and
if ([strNew1 isEqualToString:strNew2]) //Compares NSString object
{
}
Remember that isEqualToString: comes with a WARNING
[string1 isEqualToString: string2]
will effectively return false is both strings are nil.

UILabel.text doesn't change from NSString (iOS)

I'm trying to change name(UILabel).text to nameString(NSString) but it does not present it on the screen (it does change - is it the value when I debug and it the correct value).
Code:
NSString *namesString = [self.names objectAtIndex:i];
infoWindow.storeAddressLabel.text = namesString;
Note: if I type:
infoWindow.storeAddressLabel.text=#"someText";
It works
Does anybody know why is it? Thanks!
You're referencing two different variables. On the 1st line you define nameString, and then on the next line you set using namesString with an extra s
If infoWindow.storeAddressLabel.text=#"someText"; works, then storeAddressLabel object isn't nil. The only possibility can be that the string being returned from the code NSString *namesString=[self.names objectAtIndex:i]; is being returned nil. Please check and verify.
Thanks.
Try this see what happens,
NSString *nameString=[sef.names objectAtIndex:i];
if( nameString!=nil && nameString.length >0){
NSLog(#"nameString %#",nameString);
infoWindow.storeAddressLabel.text=namesString;
}
else{
infoWindow.storeAddressLabel.text=#"nameString variable was nil so I am being set as label text";
}
In correction to above code which contains many bugs, i have written following answer:
NSString *nameString = [sef.names objectAtIndex:i];
if(nameString)
{
infoWindow.storeAddressLabel.text = nameString;
}
else
{
infoWindow.storeAddressLabel.text = #"nameString variable was nil so I am being set as label text";
}
Try using a class method to assign the text property - (check for nil first of course as stringWithString: must be passed a non-nil NSString)
infoWindow.storeAddressLabel.text = [NSString stringWithString:(NSString*)[self.names objectAtIndex:i]];
If your namesString is not nil then it should be displayed..
try like this,
NSString *namesString = [self.names objectAtIndex:i];
infoWindow.storeAddressLabel.text = [NSString stringWithFormat:#"%#",namesString];

Previous NSDictionary now to JSON array

- (void)retrieveData
{
NSURL * url = [NSURL URLWithString:#"***/connection.php"];
NSData * data = [NSData dataWithContentsOfURL:url];
_json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_questionsArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _json.count; i++)
{
NSString * qID = [[_json objectAtIndex:i] objectForKey:#"id"];
NSString * qTitle = [[_json objectAtIndex:i] objectForKey:#"question_title"];
NSString * qA = [[_json objectAtIndex:i] objectForKey:#"A"];
NSString * qB = [[_json objectAtIndex:i] objectForKey:#"B"];
NSString * qC = [[_json objectAtIndex:i] objectForKey:#"C"];
NSString * qD = [[_json objectAtIndex:i] objectForKey:#"D"];
NSString * qAnswer = [[_json objectAtIndex:i] objectForKey:#"question_answer"];
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[_questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
NSLog( #"%#", _questions );
}
Logs (null)
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
If I understand your question correctly it becomes something like this
self.questions = .... //I assume this is the array you reference 'question' objects that is created by your retrieve data method
//this used to be created by pulling an object out of your questions dictionary with the key i interpreted as a string.
//now that it's an array you should be able to just reference it by index, assuming they were inserted in order
//I'm also assuming that what comes out of the aray is a question object given the code you provided with the signature
//- (id) initWithQuestionID: (NSString *) qID andQuestionName: (NSString *) qName andQuestionA: (NSString *) qA andQuestionB: (NSString *) qB andQuestionC: (NSString *) qC andQuestionD: (NSString *) qD andQuestionAnswer: (NSString *) qAnswer
Question *nextQuestion = self.questions[i];
self.answer = nextQuestion.questionAnswer;
self.questionLabel.text = nextQuestion.questionLabel;
//and so on
I also suggest the following edit to replace your for loop. It uses a for in loop instead, this saves you from having to keep track of an index and looks cleaner. It also helps so you don't keep repeating the [_json objectAtIndex:i] chunk of code. I also use modern objective-c syntax to access the dictionary.
for (NSDictionary *questionDictionary in _json)
{
NSString * qID = questionDictionary[#"id"];
NSString * qTitle = questionDictionary[#"question_title"];
...
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
If you need the key along with the object in the dictionary then you can clean it up in a similar way with the enumerateObjectsUsingBlock
[_json enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
//your code here
}];
EDIT
It sounds like what your really wanting to do is to pull down your JSON but keep all your other code the way it was when you were using a dictionary that you got from your plist. So in this case you want your parsing function to return a dictionary instead of an array. If that's the case it's worth sidestepping into computer science for a second.
NSDictionarys are also known as a hash, map, symbol table, or associative array. Some languages (such as Lua) don't have an array collection like NSArray, they only have dictionaries. From a dictionary you can create many of the other collections your used to like arrays (and sets too). Heres how it works: Instead of an ordered collection of elements with an index, you place the items in a dictionary and use what would have been the index as the key, and the value becomes, well, the value. For example an array and it's equivalent associative array (aka dictionary):
NSArray *array = #[#"hello", #"world", #"!"];
NSDictionary *dictionary = #{#(1): #"hello",
#(2): #"world",
#(3): #"!"};
This is exactly what your doing when you load in the data from your plist because the first elements key is 0 followed by another dictionary, and I'm supposing that the next element in the list is 1 followed by another dictionary. Inside your parsing function it becomes
NSMutableDictionary *questions = [[NSMutableDictionary alloc] init];
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
This of course assumes that your api is going to return the JSON questions in the order you want.

Searching dictionary works find with hard coded variable, but otherwise crashes

I have the following code to search the following dictionary:
//NSString *knownObject = #"3:40 am";
NSArray *temp = [itemDict allKeysForObject:knownObject];
NSString *key = [temp objectAtIndex:0];
NSLog(#"prayer: %#", key);
Dict:
{
asr = "4:23 pm";
dhuhr = "12:02 pm";
fajr = "1:16 am";
isha = "10:47 pm";
maghrib = "8:24 pm";
shurooq = "3:40 am";
}
When running the first line, it correctly returns "shurooq". However, when I use my variable:
NSArray *temp = [itemDict allKeysForObject:nextPrayerTime];
The log output of nextPrayerTime is simply 3:40 am as expected.
Why is this not working?
Many thanks!
Checkout the value of nextPrayerTime, if itemDict contain any object same as nextPrayerTime then [itemDict allKeysForObject:nextPrayerTime]; will return an array of keys otherwise it will return empty array.
In your current case anyhow you get an empty array but when you try to access [temp objectAtIndex:0] then compiler couldn't find any object and it gets an array out of bound exception and this cause the crash of your app....
To overcome this exception you should check the count of objects in array...
NSArray *temp = [itemDict allKeysForObject: nextPrayerTime];
if([temp count] > 0) {
NSString *key = [temp objectAtIndex:0];
NSLog(#"prayer: %#", key);
} else
NSLog(#"NO Object Found");
What is the value of nextPrayerTime?
According to the docs, allKeysForObject: returns
A new array containing the keys corresponding to all occurrences of anObject in the dictionary. If no object matching anObject is found, returns an empty array.
Based on the information given I would guess you are getting back an empty array and when you call [temp objectAtIndex:0] you are accessing an index that does not exist and getting an out of bounds exception.
I am not a 100% sure of my answer, but is the method isEqualToString used for comparison of the objects when the object is a NSString?
Otherwise you probably need to compare to the original object in the dictionary (using the same NSString).

Add values in NSMutableDictionary in iOS with Objective-C

I'm starting objective-c development and I would like to ask the best way to implement a list of keys and values.
In Delphi there is the class TDictionary and I use it like this:
myDictionary : TDictionary<string, Integer>;
bool found = myDictionary.TryGetValue(myWord, currentValue);
if (found)
{
myDictionary.AddOrSetValue(myWord, currentValue+1);
}
else
{
myDictionary.Add(myWord,1);
}
How can I do it in objective-c? Is there equivalent functions to the above mentioned AddOrSetValue() or TryGetValue()?
Thank you.
You'd want to implement your example along these lines:
EDIT:
//NSMutableDictionary myDictionary = [[NSMutableDictionary alloc] init];
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
NSNumber *value = [myDictionary objectForKey:myWord];
if (value)
{
NSNumber *nextValue = [NSNumber numberWithInt:[value intValue] + 1];
[myDictionary setObject:nextValue forKey:myWord];
}
else
{
[myDictionary setObject:[NSNumber numberWithInt:1] forKey:myWord]
}
(Note: you can't store ints or other primitives directly in a NSMutableDictionary, hence the need to wrap them in an NSNumber object, and make sure you call [myDictionary release] when you've finished with the dictionary).
The other answers are correct, but there is more modern syntax for this now. Rather than:
[myDictionary setObject:nextValue forKey:myWord];
You can simply say:
myDictionary[myWord] = nextValue;
Similarly, to get a value, you can use myDictionary[key] to get the value (or nil).
Yep:
- (id)objectForKey:(id)key;
- (void)setObject:(id)object forKey:(id)key;
setObject:forKey: overwrites any existing object with the same key; objectForKey: returns nil if the object doesn't exist.
Edit:
Example:
- (void)doStuff {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:#"Foo" forKey:#"Key_1"]; // adds #"Foo"
[dict setObject:#"Bar" forKey:#"Key_2"]; // adds #"Bar"
[dict setObject:#"Qux" forKey:#"Key_2"]; // overwrites #"Bar"!
NSString *aString = [dict objectForKey:#"Key_1"]; // #"Foo"
NSString *anotherString = [dict objectForKey:#"Key_2"]; // #"Qux"
NSString *yas = [dict objectForKey:#"Key_3"]; // nil
}
Reedit: For the specific example there exists a more compact approach:
[dict
setObject:
[NSNumber numberWithInteger:([[dict objectForKey:#"key"] integerValue] + 1)]
forKey:
#"key"
];
Crazy indentation for readability.

Resources