iOS—NSDictionary has 6 keys, shows 3 - ios

For a task tracking app, I'm using an array of NSDictionaries that contain certain details about the task—-duration, etc.
NSString *savedTaskName = addedTask.taskName;
NSString *savedTaskAccountName = addedTask.accountName;
NSString *savedTaskBillCode = addedTask.billCode;
NSString *savedTaskActivityCode = addedTask.activityCode;
NSNumber *savedTaskDuration = [NSNumber numberWithFloat:addedTask.taskDuration];
NSString *savedTaskTimeCode = addedTask.formattedTimeString;
NSDictionary *addedTaskData = [[NSDictionary alloc] initWithObjectsAndKeys:savedTaskName, kTaskName,
savedTaskAccountName, kAccountName,
savedTaskBillCode, kBillCode,
savedTaskActivityCode, kActivityCode,
savedTaskDuration, kTaskDuration,
savedTaskTimeCode, kTimeCode, nil];
Perhaps I'm making a newb mistake, but when I go to add addedTaskData to an NSMutableArray, the array only catches the first three values and keys of the dictionary.
Have I lost my mind? Why isn't it catching all? Curiously, asking for the count of dictionary keys returns the full amount.

initWithObjectsAndKeys stops when it hits a nil value. If you're getting three keys/values, then:
NSDictionary *addedTaskData = [[NSDictionary alloc] initWithObjectsAndKeys:savedTaskName, kTaskName,
savedTaskAccountName, kAccountName,
savedTaskBillCode, kBillCode,
savedTaskActivityCode, kActivityCode, // <-- THIS IS NIL
savedTaskDuration, kTaskDuration,
savedTaskTimeCode, kTimeCode, nil];
You can use something like this instead:
NSDictionary *addedTaskData = [[NSDictionary alloc] initWithObjectsAndKeys:savedTaskName, kTaskName,
savedTaskAccountName, kAccountName,
savedTaskBillCode, kBillCode,
(savedTaskActivityCode ?: [NSNull null]), kActivityCode, // Or #""
savedTaskDuration, kTaskDuration,
savedTaskTimeCode, kTimeCode, nil];
Anything that could be nil should be protected like this.
If you're doing a bunch of them in a row, you can declare a local null variable:
id null = [NSNull null];
NSDictionary *addedTaskData = [[NSDictionary alloc] initWithObjectsAndKeys:(savedTaskName ?: null), kTaskName,
(savedTaskAccountName ?: null), kAccountName,
(savedTaskBillCode ?: null), kBillCode,
(savedTaskActivityCode ?: null), kActivityCode, // Or #""
(savedTaskDuration ?: null), kTaskDuration,
(savedTaskTimeCode ?: null), kTimeCode, nil];

When not using the literal syntax there is a chance that you will run into nil termination issues. If savedTaskDuration is actually nil then it's going to chop off the rest of the NSDictionary.
Take a look with the debugger and see if you have a nil value in there somewhere. Then run the Modern Objective-C refactor tool and convert that sucker to a NSDictionary literal so that you are protected from this in the future.
For reference the Literal syntax is like this:
NSDictionary *addedTaskData = #{#"Key" : #"Value", #"AnotherKey" : #"AnotherValue"};

Try to use literals like this:
NSString *savedTaskName = addedTask.taskName;
NSString *savedTaskAccountName = addedTask.accountName;
NSString *savedTaskBillCode = addedTask.billCode;
NSString *savedTaskActivityCode = addedTask.activityCode;
NSNumber *savedTaskDuration = [NSNumber numberWithFloat:addedTask.taskDuration];
NSString *savedTaskTimeCode = addedTask.formattedTimeString;
// Use literals and see your code doesn't crash
NSDictionary *addedTaskData = #{kTaskName: savedTaskName,
kAccountName: savedTaskAccountName,
savedTaskBillCode: savedTaskBillCode,
savedTaskActivityCode: savedTaskActivityCode
savedTaskDuration: savedTaskDuration
savedTaskTimeCode: savedTaskTimeCode};
And see your code doesn't crash

Related

xcode - set value of a nested object

I have a json from a service and I need to change the values of one obeject.
{
question = (
{
answer = (
{
Id = 1;
value = 1;
},
{
Id = 2;
value = 0;
}
);
},
.....
I use that code to directly access to the second "value" element and set it to "true"
NSMutableDictionary * dict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSMutableDictionary *preguntasDict = [[NSMutableDictionary alloc] init];
preguntasDict =[[[dict valueForKey:#"question"]mutableCopy];
NSMutableDictionary *answer = [[NSMutableDictionary alloc] init];
respuestasDict =[[[[preguntasDict valueForKey:#"answer"]objectAtIndex:0]objectAtIndex:1] mutableCopy];
[respuestasDict setObject:[NSNumber numberWithBool:true] forKey:#"value"];
It works: "respuestasDict" changes but the whoole "dict" not.
My question is: how could rebuild the entire dictionary? or it is possible to access directly to the nested object and change it?
Note that perguntasDict and respuestasDict are mutable copies of your dictionaries, so basically you are editing copies of your dict. You need to access dict directly, like this:
NSArray *answers = dict[#"question"][#"answer"];
[answers firstObject][#"value"] = #(YES);
PS: Doing dict[#"question"] is the same thing as [dict objectForKey:#"question"]. Also, #(YES) is the same thing as [NSNumber numberWithBool:true]

What is the best method to break this into usable strings

Whats is the best way to parse this out?
String:
UMversion=2.9&UMstatus=Approved&UMauthCode=152058&UMrefNum=59567592&UMavsResult=Address%3A%20Match%20%26%205%20Digit%20Zip%3A%20Match&UMavsResultCode=YYY&UMcvv2Result=Match&UMcvv2ResultCode=M&UMresult=A&UMvpasResultCode=&UMerror=Approved&UMerrorcode=00000&UMcustnum=&UMbatch=1&UMbatchRefNum=91016&UMisDuplicate=N&UMconvertedAmount=&UMconvertedAmountCurrency=840&UMconversionRate=&UMcustReceiptResult=No%20Receipt%20Sent&UMprocRefNum=&UMcardLevelResult=A&UMauthAmount=10&UMfiller=filled
I get this back from the web service as one big long string. Each of the variables are listed then they have a = sign then what I need to populate the variable with.
I need to get all of this data into variables to check them.
So, how should I go about breaking it down.
Use this kind of code:
NSArray* components = [veryLongString componentsSeparatedByString:#"&"]; // array of strings like "x=y"
NSMutableDictionary* parsedResult = [NSMutableDictionary new];
for (NSString* keyValuePair in components) {
NSArray* keyAndValue = [keyValuePair componentsSeparatedByString:#"="];
NSString* key = keyAndValue[0];
NSString* value = (keyAndValue.count>1) ? keyAndValue[1] : nil;
// remove percent escapes in case we have URL-encoded characters in the value like '%20' and the like
parsedResult[key] = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] ?: [NSNull null];
}
NSLog(#"dictionary of parameters: %#", parsedResult);
You will end up with a dictionary containing the keys and values extracted from your string.
NSString* firstPass = [sourceString stringByReplacingOccurrencesOfString:#"&" withString:#"\",\""];
NSString* secondPass = [firstPass stringByReplacingOccurrencesOfString:#"=" withString:#"\":\""];
NSString* grandFinale = [NSString stringWithFormat:#"{\"%#\"}"];
NSData* jsonSource = [grandFinale dataUsingEncoding:NSUTF8Encoding];
NSError* error = nil;
NSDictionary* theBiggie = [NSJSONSerialization JSONObjectWithData:jsonSource options:0 error:&error];
I think NSJSONSerialization will automagically fix up the percent encoding. If not, run grandFinale through stringByRemovingPercentEncoding.

IF statement issue in IOS using NSString

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.

Why aren't these NSString instance variables allocated?

first post here. I was reading through an Objective-C tutorial earlier, and I saw that they had made a couple of NSString instance variables like this:
#implementation MyViewController {
NSString *stringOne;
NSString *stringTwo;
NSString *stringThree;
NSString *stringFour;
NSString *stringFive;
}
And then simply used them in ViewDidLoad like this:
- (void)viewDidLoad
{
[super viewDidLoad];
stringOne = #"Hello.";
stringTwo = #"Goodbye.";
stringThree = #"Can't think of anything else to say.";
stringFour = #"Help...";
stringFive = #"Pheww, done.";
}
How have they done this without instantiating the string? Why does this work? Surely you'd have to do something like stringOne = [NSString stringFromString:#"Hello."]; to properly alloc and init the object before you could simply do stringOne= #"Hello.";.
Sorry if this a dumb question, but I find these little things throw me.
Thanks,
Mike
From the Apple String Programming Guide:
Creating Strings
The simplest way to create a string object in source code is to use the Objective-C #"..." construct:
NSString *temp = #"Contrafibularity";
Note that, when creating a string constant in this fashion, you should use UTF-8 characters. Such an object is created at compile time and exists throughout your program’s execution. The compiler makes such object constants unique on a per-module basis, and they’re never deallocated. You can also send messages directly to a string constant as you do any other string:
BOOL same = [#"comparison" isEqualToString:myString];
String constants like #"Hello" are already allocated and initialized for you by the compiler.
Just remember this basic thing:-
NSString *string = ...
This is a pointer to an object, "not an object"!
Therefore, the statement: NSString *string = #"Hello"; assigns the address of #"Hello" object to the pointer string.
#"Hello" is interpreted as a constant string by the compiler and the compiler itself allocates the memory for it.
Similarly, the statement
NSObject *myObject = somethingElse;
assigns the address of somethingElse to pointer myObject, and that somethingElse should already be allocated and initialised.
Therefore, the statement: NSObject *myObject = [[NSObject alloc] init]; allocates and initializes a NSObject object at a particular memory location and assigns its address to myObject.
Hence, myObject contains address of an object in memory, for ex: 0x4324234.
Just see that we are not writing "Hello" but #"Hello", this # symbol before the string literal tells the compiler that this is an object and it returns the address.
I hope this would answer your question and clear your doubts. :)
actually this can be said "syntactic sugar". there are some other type of NS object that can be creatable without allocation or formatting.
e.g:
NSNumber *intNumber1 = #42;
NSNumber *intNumber2 = [NSNumber numberWithInt:42];
NSNumber *doubleNumber1 = #3.1415926;
NSNumber *doubleNumber2 = [NSNumber numberWithDouble:3.1415926];
NSNumber *charNumber1 = #'A';
NSNumber *charNumber2 = [NSNumber numberWithChar:'A'];
NSNumber *boolNumber1 = #YES;
NSNumber *boolNumber2 = [NSNumber numberWithBool:YES];
NSNumber *unsignedIntNumber1 = #256u;
NSNumber *unsignedIntNumber2 = [NSNumber numberWithUnsignedInt:256u];
NSNumber *floatNumber1 = #2.718f;
NSNumber *floatNumber2 = [NSNumber numberWithFloat:2.718f];
// an array with string and number literals
NSArray *array1 = #[#"foo", #42, #"bar", #3.14];
// and the old way
NSArray *array2 = [NSArray arrayWithObjects:#"foo",
[NSNumber numberWithInt:42],
#"bar",
[NSNumber numberWithDouble:3.14],
nil];
// a dictionary literal
NSDictionary *dictionary1 = #{ #1: #"red", #2: #"green", #3: #"blue" };
// old style
NSDictionary *dictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:#"red", #1,
#"green", #2,
#"blue", #3,
nil];
for more information, see "Something wonderful: new Objective-C literal syntax".

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