How can I convert the values is an NSDictionary into an NSString? - objective-c

I have a method in Objective-c that takes in an NSDictionary and returns the values as a space-separated NSString. This application is cross-platform, and as such I cannot use fast enumeration. This is what I have so far, followed by the output (which shows that the String is never created):
-(NSString *)stringValuesFromDict:(NSDictionary *map)
{
NSArray *values = [map allValues];
NSString *params = [NSString string];
NSLog(#"values length: %d", [values count]);
NSLog(#"values = %#", [values description]);
for (int i = 0; i < [values count]; i++)
{
[params stringByAppendingString:[values objectAtIndex:i]];
[params stringByAppendingString:#" "];
}
NSLog(#"params = %#", params);
return params;
}
The NSDictionary:
{"arg1"="monkey"}
The output:
values length: 1
values = (
monkey
)
params =
What am I doing wrong? How can I get params to be set to monkey?

If I read the question correctly, all you need is
[[dict allValues] componentsJoinedByString:#" "]

You need a mutable string. Change this:
NSString *params = [NSString string];
to this:
NSMutableString *params = [NSMutableString string];
Then change these:
[params stringByAppendingString:[values objectAtIndex:i]];
[params stringByAppendingString:#" "];
to these:
[params appendString:[values objectAtIndex:i]];
[params appendString:#" "];

stringByAppendingString: returns a new string. So you would have to do something like this:
params = [params stringByAppendingString:[values objectAtIndex:i]];
But the disadvantage is, that every time a new string is created, which is wasting memory
You should propably use a NSMutableString instead. And then you can just call
[params appendString:[values objectAtIndex:i]];

Related

FatSecret API "invalid signature"

Using this repository I was not able to make the queries work when oauth_token must be provided. I always get invalid signature. Tried a lot of solutions and tweaks in the code, nothing worked. Please help.
This is the code from the git mentioned:
NSString *OAuthorizationHeader(NSURL *url, NSString *method, NSData *body, NSString *_oAuthConsumerKey, NSString *_oAuthConsumerSecret, NSString *_oAuthToken, NSString *_oAuthTokenSecret)
{
NSString *_oAuthNonce = [NSString ab_GUID];
NSString *_oAuthTimestamp = [NSString stringWithFormat:#"%d", (int)[[NSDate date] timeIntervalSince1970]];
NSString *_oAuthSignatureMethod = #"HMAC-SHA1";
NSString *_oAuthVersion = #"1.0";
NSMutableDictionary *oAuthAuthorizationParameters = [NSMutableDictionary dictionary];
[oAuthAuthorizationParameters setObject:_oAuthNonce forKey:#"oauth_nonce"];
[oAuthAuthorizationParameters setObject:_oAuthTimestamp forKey:#"oauth_timestamp"];
[oAuthAuthorizationParameters setObject:_oAuthSignatureMethod forKey:#"oauth_signature_method"];
[oAuthAuthorizationParameters setObject:_oAuthVersion forKey:#"oauth_version"];
[oAuthAuthorizationParameters setObject:_oAuthConsumerKey forKey:#"oauth_consumer_key"];
if(_oAuthToken)
[oAuthAuthorizationParameters setObject:_oAuthToken forKey:#"oauth_token"];
// get query and body parameters
NSDictionary *additionalQueryParameters = [NSURL ab_parseURLQueryString:[url query]];
NSDictionary *additionalBodyParameters = nil;
if(body) {
NSString *string = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
if(string) {
additionalBodyParameters = [NSURL ab_parseURLQueryString:string];
}
}
// combine all parameters
NSMutableDictionary *parameters = [oAuthAuthorizationParameters mutableCopy];
if(additionalQueryParameters) [parameters addEntriesFromDictionary:additionalQueryParameters];
if(additionalBodyParameters) [parameters addEntriesFromDictionary:additionalBodyParameters];
// -> UTF-8 -> RFC3986
NSMutableDictionary *encodedParameters = [NSMutableDictionary dictionary];
for(NSString *key in parameters) {
NSString *value = [parameters objectForKey:key];
[encodedParameters setObject:[value ab_RFC3986EncodedString] forKey:[key ab_RFC3986EncodedString]];
}
NSArray *sortedKeys = [[encodedParameters allKeys] sortedArrayUsingFunction:SortParameter context:(__bridge void *)(encodedParameters)];
NSMutableArray *parameterArray = [NSMutableArray array];
for(NSString *key in sortedKeys) {
[parameterArray addObject:[NSString stringWithFormat:#"%#=%#", key, [encodedParameters objectForKey:key]]];
}
NSString *normalizedParameterString = [parameterArray componentsJoinedByString:#"&"];
NSLog(#"normalizedParameters: %#", normalizedParameterString);
NSString *normalizedURLString;
if ([url port] == nil) {
normalizedURLString = [NSString stringWithFormat:#"%#://%#%#", [url scheme], [url host], [url path]];
} else {
normalizedURLString = [NSString stringWithFormat:#"%#://%#:%#%#", [url scheme], [url host], [url port], [url path]];
}
NSString *signatureBaseString = [NSString stringWithFormat:#"%#&%#&%#",
[method ab_RFC3986EncodedString],
[normalizedURLString ab_RFC3986EncodedString],
[normalizedParameterString ab_RFC3986EncodedString]];
NSLog(#"signature base: %#", signatureBaseString);
NSString *key = [NSString stringWithFormat:#"%#&%#&",
[_oAuthConsumerSecret ab_RFC3986EncodedString],
[_oAuthTokenSecret ab_RFC3986EncodedString]];
NSLog(#"key codes: %#", key);
NSData *signature = HMAC_SHA1(signatureBaseString, key);
NSString *base64Signature = [signature base64EncodedString];
// PARKER CHANGE: changed oAuthAuthorizationParameters to parameters
NSMutableDictionary *authorizationHeaderDictionary = [parameters mutableCopy];
[authorizationHeaderDictionary setObject:base64Signature forKey:#"oauth_signature"];
NSMutableArray *authorizationHeaderItems = [NSMutableArray array];
for(NSString *key in authorizationHeaderDictionary) {
NSString *value = [authorizationHeaderDictionary objectForKey:key];
NSLog(#"KEY: %#", key);
NSLog(#"VALUE: %#", value);
// PARKER CHANGE: removed quotes that surrounded each value
[authorizationHeaderItems addObject:[NSString stringWithFormat:#"%#=%#",
[key ab_RFC3986EncodedString],
[value ab_RFC3986EncodedString]]];
}
// PARKER CHANGE: changed concatentation string from ", " to "&"
NSString *authorizationHeaderString = [authorizationHeaderItems componentsJoinedByString:#"&"];
// authorizationHeaderString = [NSString stringWithFormat:#"OAuth %#", authorizationHeaderString];
NSLog(#"final: %#", authorizationHeaderString);
return authorizationHeaderString;
}
And this is how I'm calling it:
- (void) makeUserRequestWithMethod:(NSString *)method
parameters:(NSDictionary *)params
completion:(void (^)(NSDictionary *data))completionBlock {
NSLog(#"%s", __func__);
NSMutableDictionary *parameters = [params mutableCopy];
[parameters addEntriesFromDictionary:[self defaultParameters]];
[parameters addEntriesFromDictionary:#{ #"method" : method }];
NSString *queryString = [self queryStringFromDictionary:parameters];
NSData *data = [NSData dataWithBytes:[queryString UTF8String] length:queryString.length];
NSString *authHeader = OAuthorizationHeader([NSURL URLWithString:FAT_SECRET_API_ENDPOINT],
#"POST",
data,
_oauthConsumerKey,
_oauthConsumerSecret,
_oAuthToken,
_oAuthTokenSecret
);
NSLog(#"header: %#", authHeader);
NSURL *url = [NSURL URLWithString:[FAT_SECRET_API_ENDPOINT stringByAppendingFormat:#"?%#", authHeader]];
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
id JSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
completionBlock(JSON);
} else {
completionBlock(nil);
}
}] resume];
}
I'm going to go out on a limb here and say that the code that others have proven to work is probably not at fault here, but rather first look at your own code. You say the error messages is "Invalid Signature" which sounds like it's a warning from the server you're calling, not from the code you're using.
[parameters addEntriesFromDictionary:[self defaultParameters]];
[parameters addEntriesFromDictionary:#{ #"method" : method }];
NSString *queryString = [self queryStringFromDictionary:parameters];
What happens inside [self defaultParameters] and are you 100% certain the parameters are appropriate (including spelling) for the API you're calling?
Have you verified that [self queryStringFromDictionary:parameters] prepares a properly formatting query string, and isn't introducing some error (non-escaped special characters, or percent encoding, for example)?

How to get the date and title and description alone from this parsing result

How to get the date and title and description alone from this parsing result:
title = "Inter-views 29/03/2017 random description here (its in arabic);
title = " \U0627\U062e\U0628\U0627\U0631 \U0627\U0644\U0635\U0628\U0627\U062d 14/04/2017";
That's the parsing from same api but different (didSelectRowAtIndexPath).
I'm currently using this code but as I notified the parsing is different. So I cannot use static logic.
Code:
NSString *myString = [item objectForKey:#"title"];
NSMutableArray *myArray = [[NSMutableArray alloc]
initWithArray:[myString componentsSeparatedByCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:#" "]]];
NSString *Category = #"";
if(myArray.count>=1){
Category = [myArray objectAtIndex:0];
[myArray removeObjectAtIndex:0];
}
NSString *Date = #"";
if(myArray.count>=1) {
Date = [myArray objectAtIndex:0];
[myArray removeObjectAtIndex:0];
}
cell.dateLabel.text = [[NSString alloc] initWithFormat:#"%#", [Utils dateTransform:[Date stringByReplacingOccurrencesOfString:#" " withString:#""] FromFormat:#"dd-MM-yyyy" ToFormat:#"dd-MMMM-yyyy"]];
NSString *Title = #"";
for (NSString *word in myArray) {
Title = [Title stringByAppendingString:word];
Title = [Title stringByAppendingString:#" "];
}
cell.titleAndDescLabel.text =[NSString stringWithFormat:#"%# %#", Category,Title];
Your have to use for loop to get value
for (NSInteger i=0; i<[[jsonObject objectForKey:#"yourMainKey"]count]; i++)
{
NSString *strname =[[[jsonObject objectForKey:#"yourMainKey"]objectAtIndex:i]objectForKey:#"date"];
NSLog(#"strname==%#, strname);
NSString *str title =[[[jsonObject objectForKey:#"yourMainKey"]objectAtIndex:i]objectForKey:#"title"];
NSLog(#"strtitle ==%#, strtitle);
NSString *str description =[[[jsonObject objectForKey:#"yourMainKey"]objectAtIndex:i]objectForKey:#"description"];
NSLog(#"description ==%#, description);
}

Vary number of objects in array

In my app the user can create unlimited UITextFields. then I get the information of all of them and upload in a json file:
NSString *object;
NSString *object2;
NSString *object3;
NSString *object4;
NSString *object5;
NSString *size;
for (UITextField *text in array2) {
int touchedtag = text.tag;
NSUInteger tagCount = touchedtag;
switch (tagCount) {
case 1:
object = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 2:
object2 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 3:
object3 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 4:
object4 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
case 5:
object5 = [NSString stringWithFormat:#"%# %# %#", text.text, NSStringFromCGRect(text.frame), text.font];
break;
default :
break;
}
}
NSArray *keys = [NSArray arrayWithObjects:#"text", #"text2", #"text3", #"text4", #"text5", nil];
NSArray *objects = [NSArray arrayWithObjects:object,object2,object3, object4, object5, nil];
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSString* jsonString = [jsonDictionary JSONRepresentation];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
[jsonData writeToFile:path atomically:YES];
NSString *destDir = #"/sandbox/";
[[self restClient] uploadFile:filename toPath:destDir
withParentRev:nil fromPath:path];
[[self restClient] loadMetadata:#"/sandbox/"];
}
The problem is that the number of objects is not independent (object, object2...). so if the user creates less than 5 text fields the app crashes, while if more than 5 nothing happens, but the info for tag 6 and on is not recorded. How can I vary the number of objects according to the number of fields created?
Learn how to use NSMutableArray: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
It allows you to add and remove objects "on the fly", just like using a Vector in many other languages. (Java, ActionScript, C++...)
Example:
(someValue is a previously declared variable)
stuff = [[NSMutableArray alloc] initWithCapacity: someValue];
[stuff insertObject:object1 atIndex:0];
[stuff insertObject:object2 atIndex:1];
[stuff insertObject:object2 atIndex:2];
...
This code is to initialize it with a specific number of objects, which seeems to be what you are looking for. NSMutableArray also allows you to adjust the number of objects in the array at a later time.

Strange __block storage variable crash

I have a problem in my code which I have distilled down to the following (silly) example
NSArray *array = [NSArray arrayWithObjects:#"1", #"2", #"3", nil];
__block NSString *a = #"-1";
[array enumerateObjectsUsingBlock:^(id whoCares, NSUInteger idx, BOOL *stop) {
a = [NSString stringWithFormat:#"%# %d", a, idx];
NSLog(#"%#", a);
}];
NSLog(#"%#", a);
This code works, but if I comment out the first NSLog (within the block) the code crashes. But, if I change the format string to the following
a = [NSString stringWithFormat:#"%d", idx];
then the code runs fine without the NSLog within the block.
What is going on here? I hope I am just misunderstanding something.
stringWithFormat: gives you an autoreleased object, which you're not retaining. By the time the block exits and you call NSLog, a might have already been deallocated.
One solution might be to use a mutable string and append to it each time instead of reassigning.
NSArray *array = [NSArray arrayWithObjects:#"1", #"2", #"3", nil];
NSMutableString *a = [NSMutableString stringWithFormat:#"-1"];
[array enumerateObjectsUsingBlock:^(id whoCares, NSUInteger idx, BOOL *stop) {
[a appendFormat:#" %d", idx];
}];
NSLog(#"%#", a);

stringWithContentsofurl generate leaks

im trying to get data via
NSString *string = [NSString
stringWithContentsOfURL: [NSURL
URLWithString: url]];
everything works fine, but when I run it with performance tool, it finds leaks on this line.
Here is whole mehod I use:
- (NSMutableDictionary *) getOutputImagesData: (URLParserImagesData) data
{
NSString *type = (data == URLParserImagesDataLatestImage) ? #"img" : #"size";
NSString *url = [NSString stringWithFormat: #"%s%s%s", [URLParserSiteURLforCategories UTF8String], [URLParserType UTF8String], [type UTF8String]];
//i get leaks here
NSString *string = [NSString stringWithContentsOfURL: [NSURL URLWithString: url]];
NSArray *imagesTemp = [string componentsSeparatedByString: #","];
NSMutableDictionary *outputImages = [NSMutableDictionary dictionary];
for(NSString *img in imagesTemp)
{
NSArray *splitStrings = [img componentsSeparatedByString: #"="];
if(data == URLParserImagesDataImagesCount)
{
NSNumber *integerValue = [NSNumber numberWithInt: [[splitStrings objectAtIndex:1] intValue]];
[outputImages setObject: integerValue forKey: [splitStrings objectAtIndex:0]];
}
else
[outputImages setObject: [splitStrings objectAtIndex:1] forKey: [splitStrings objectAtIndex:0]];
}
return outputImages;
}
There is nothing wrong in your code.
stringWithContentsOfURL: is deprecated. Try to use stringWithContentsOfURL:encoding:error: or stringWithContentsOfURL:usedEncoding:error: instead.