Problem Comparing Value Returned by valueForKey Method of NSDictionary - nsdictionary

I use TouchJSON to convert the following to an NSDictionary object
// sample JSON object
{
data = (
{
companyId = 4779;
companyName = "ABC Corporation";
},
{
companyId = 4806;
companyName = "EFG Corporation";
}
);
dataCount = 2;
success = 1;
}
NSString *src = #"http://mysite/app1/companyList.php";
NSURL *url = [[NSURL alloc] initWithString:src];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
NSError *error = nil;
NSDictionary *dic = [[CJSONDeserializer deserializer] deserializeAsDictionary:data error:&error];
int dataCount = (int) [dic valueForKey:#"dataCount"];
// This is where I have problem with. The (dataCount == 1) expression never evaluates to true even if value of dataCount from the JSON object equals to 1.
if ( dataCount == 1 ){
// do something
} else {
// do something else
}
Have I done anything wrong?
I'd appreciate any help.

[dic valueForKey:#"dataCount"] will be an NSNumber. You'll need to call -intValue on it to get an int.
int dataCount = [[dic valueForKey:#"dataCount"] intValue];
if (dataCount == 1) {
...
}

Related

How can i fetch value from Json response in Objective -C

I have a problem with fetching data from Json response.
Here is an example data structure :
(
{
AT = "<null>";
DId = 0;
DO = 0;
PLId = 33997;
PdCatList = (
{
PLId = 33997;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
}
);
PdId = 0;
SId = 0;
Sec = 1;
},
{
AT = "<null>";
DId = 0;
DO = 11;
Dis = 0;
PLId = 34006;
PdCatList = (
{
PLId = 34006;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119830;
},
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
},
{
PLId = 33997;
PPCId = 0;
pdList = (
{
IsDis = 0;
IsPS = 0;
IsT = 1;
PA = 1;
PCId = 119777;
}
);
}
);
PdId = 0;
SId = 0;
Sec = 1;
},
)
how would i parse the resulting Structure ?
I would like to get a list of values directly. What if i have several values in a tupel for example performer PdCatList, pdList. How would i access those values?
Can anyone help me
Thank's
my code is
NSError *error;
Array1 = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for(int i=0;i<[Array1 count];i++)
{
NSDictionary *dict1 = [Array1 objectAtIndex:i];
NSLog(#"Array1.....%#",dict1);
Array2=[dict1 valueForKey:#"PdCatList"];
for(int i=0;i<[Array2 count];i++)
{
NSDictionary *dict2 = [Array2 objectAtIndex:i];
NSLog(#"Array2.....%#",dict2);
Array3=[dict2 valueForKey:#"pdList"];
for(int i=0;i<[Array3 count];i++)
{
NSDictionary *dict3 = [Array3 objectAtIndex:i];
NSLog(#"Array3.....%#",dict3);
}
}
}
Try this...
NSError *error;
Array1 = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
for(int i=0;i<[Array1 count];i++)
{
NSDictionary *dict1 = [Array1 objectAtIndex:i];
ATArray =[dict1 valueForKey:#"AT"];
DIdArray =[dict1 valueForKey:#"DId"];
DOArray =[dict1 valueForKey:#"DO"];
PLIdArray =[dict1 valueForKey:#"PLId"];
etc...
Array2=[dict1 valueForKey:#"PdCatList"];
for(int i=0;i<[Array2 count];i++)
{
NSDictionary *dict2 = [Array2 objectAtIndex:i];
PLIdArray =[dict2 valueForKey:#"PLId"];
PPCIdArray =[dict2 valueForKey:#"PPCId"];
etc…
Array3=[dict2 valueForKey:#"pdList"];
for(int i=0;i<[Array3 count];i++)
{
NSDictionary *dict3 = [Array3 objectAtIndex:i];
IsDisArray =[dict3 valueForKey:#"IsDis"];
IsPSArray =[dict3 valueForKey:#"IsPS"];
IsTArray =[dict3 valueForKey:#"IsT"];
PAArray =[dict3 valueForKey:#"PA"];
PCIdArray =[dict3 valueForKey:#"PCId"];
}
}
}
I think what you require here is to understand what a JSON response is rather than the Answer to get the values of some objects from your JSON response.
If you want some detail explanation about JSON Parsing then you can take a look at NSJSONSerialization Class Reference. Everything is given there or you can take a look at my Answer.
Understand the Concept. It depends on what you have inside your JSON. If it's an Array ( Values inside [ ]) then you have to save in NSArray, if it's a Dictionary ( Values inside { }) then save as NSDictionary and if you have single values like string , integer, double then you have to save them using appropriate Objective-C Data types.
For some simple details with example , you can check my Answer from this question.
Use JSONKit(https://github.com/johnezang/JSONKit):
NSString *yourJSONString = ...
NSArray *responseArray = [yourJSONString objectFromJSONString];
for(NSDictionary *responseDictionary in responseArray)
{
NSString *atString = [responseDictionary objectForKey:#"AT"];
...
NSArray *pdCatListArray = [responseDictionary objectForKey:#"PdCatList"];
...here you can get all values you want,if you want to get more details in PdCatList,use for in pdCatListArray ,you can do what you want.
}
Use following method:
NSDictionary *mainDict;
SBJSON *jsonParser = [[SBJSON alloc]init];
if([[jsonParser objectWithString:responseString] isKindOfClass:[NSDictionary class]])
{
mainDict=[[NSDictionary alloc]initWithDictionary:[jsonParser objectWithString:responseString]];
}
NSDictionary *firstDict=[NSDictionary alloc]initWithDictionary:[mainDict valueForKey:#""];
You have to add JSON framework which is parse string into NSDictionary.
Use zip file from here
Open Folder and rename Classes folder to "JSON".
Copy JSON Folder and include in your project.
Import header file like below in controller where you want to parse JSON String.
#import "SBJSON.h"
#import "NSString+SBJSON.h"
Now, Parse your response string in to NSDictionary like below.
NSMutableDictionary *dictResponse = [strResponse JSONValue];
You can use KVC
to access the nested properties in the JSON. You need to know about KVC and dot syntax and Collection operators
Frameworks that map JSON to objects, such as RestKit rely heavily on KVC.
Following your sample, you could get a list of all PdCatList objects:
//sample data
NSArray *json = #[
#{#"PLId" : #33997,
#"PdCatList" : #{#"PLId": #33998,
#"PPCId" : #1,
#"pdList" : #{
#"PCId" : #119777
}}
},
#{#"PLId" : #33999,
#"PdCatList" : #{#"PLId": #4444,
#"PPCId" : #0,
#"pdList" : #{
#"PCId" : #7777
}}}
];
//KVC
NSArray *pdCatLists = [json valueForKeyPath:#"#unionOfObjects.PdCatList"];
With this you can, for example, make a very basic object mapping (which does not take care of relationships)
In PdCatList.h
#interface PdCatList : NSObject
#property (readonly, strong, nonatomic) NSNumber *PLId;
#property (readonly, strong, nonatomic) NSNumber *PPCId;
+ (instancetype)listWithDictionary:(NSDictionary *)aDictionary;
#end
In PdCatList.m
#implementation PdCatList
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
#try {
[super setValue:value forUndefinedKey:key];
}
#catch (NSException *exception) {
NSLog(#"error setting undefined key: %#, exception: %#", key, exception);
};
}
+ (id)listWithDictionary:(NSDictionary *)aDictionary
{
PdCatList *result = [[self alloc] init];
[result setValuesForKeysWithDictionary:aDictionary];
return result;
}
#end
After getting the json object
NSArray *pdCatLists = [json valueForKeyPath:#"#unionOfObjects.PdCatList"];
[pdCatLists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
PdCatList *each = [PdCatList listWithDictionary:obj];
}];
However, If what you want is just to flatten the json, you must use recursion and create a category similar to the following.
In NSJSONSerialization+FlattenedJSON.h
#interface NSJSONSerialization (FlattenedJSON)
+ (void)FlattenedJSONObjectWithData:(NSData *)data completionSuccessBlock:(void(^)(id aJson))onSuccess failure:(void(^)(NSError *anError))onFailure;
#end
In NSJSONSerialization+FlattenedJSON.m
#import "NSJSONSerialization+FlattenedJSON.h"
#implementation NSJSONSerialization (FlattenedJSON)
+ (void)FlattenedJSONObjectWithData:(NSData *)data completionSuccessBlock:(void (^)(id))onSuccess failure:(void (^)(NSError *))onFailure
{
NSError *error;
id object = [self JSONObjectWithData:data
options:kNilOptions
error:&error];
if (error)
{
onFailure(error);
}
else
{
NSMutableArray *result = [NSMutableArray array];
[self flatten:object
inArray:result];
onSuccess([result copy]);
}
}
+ (void)flatten:(id)anObject inArray:(NSMutableArray *)anArray
{
if ([anObject isKindOfClass:NSDictionary.class])
{
[((NSDictionary *)anObject) enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[self flatten:obj inArray:anArray];
}];
}
else if ([anObject isKindOfClass:NSArray.class])
{
[((NSArray *)anObject) enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self flatten:obj inArray:anArray];
}];
}
else
{
[anArray addObject:anObject];
}
}
#end

How to extract specific data has equal value for some key from NSDictionary into a combined NSArray

Right now i have a dictionary like this, it's just a example, i got A to Z:
(
{
id = 13;
name = "Roll";
firstLetter = R;
},
{
id = 14;
name = "Scroll";
firstLetter = S;
},
{
id = 16;
name = "Rock";
firstLetter = R;
},
{
id = 17;
name = "Start";
firstLetter = S;
}
)
I want to extract the dict has the same firstLetter and combine these into a NSArray object. The expected results like this:
R array:
(
{
id = 13;
name = "Roll";
firstLetter = R;
},
{
id = 16;
name = "Rock";
firstLetter = R;
}
)
and S array:
(
{
id = 14;
name = "Scroll";
firstLetter = S;
},
{
id = 17;
name = "Start";
firstLetter = S;
}
)
How to do that?
I believe the better method would be the one suggested by Saohooou
But it can be optimised as
NSArray *array = #[#{#"id": #13,#"name":#"Roll",#"firstLetter":#"R"},
#{#"id": #14,#"name":#"Scroll",#"firstLetter":#"S"},
#{#"id": #15,#"name":#"Rock",#"firstLetter":#"R"},
#{#"id": #16,#"name":#"Start",#"firstLetter":#"S"}];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
NSString *key = dict[#"firstLetter"];
NSMutableArray *tempArray = dictionary[key];
if (!tempArray) {
tempArray = [NSMutableArray array];
}
[tempArray addObject:dict];
dictionary[key] = tempArray;
}];
NSLog(#"%#",dictionary);
NSMutableDictionay *dic = [NSMutableDictionay dictionay];
for ( YourObject *obj in yourDic.allValues )
{
NSMutableArray *dateArray = dic[obj.firstLetter];
if ( !dateArray )
{
dateArray = [NSMutableArray array];
[dic setObject:dateArray forKey:obj.firstLetter];
}
[dateArray addObject:obj];
}
so dic is what you want
I assume you organized the dict as an NSArray.
NSMutableDictionary* result = [NSMutableDictionary dictionary]; // NSDictionary of NSArray
for (id entry in dict) {
NSString* firstLetter = [entry firstLetter];
// Find the group of firstLetter
NSMutableArray* group = result[firstLetter];
if (group == nil) {
// No such group --> create new a new one and add it to the result
group = [NSMutableArray array];
result[firstLetter] = group;
}
// Either group has existed, or has been just created
// Add the entry to it
[group addObject: entry];
}
result holds what you want.
try this
NSString *currentStr;
//this int is to detect currentStr
NSInteger i;
NSMutableArray* R_Array = [[NSMutableArray alloc] init];
NSMutableArray* S_Array = [[NSMutableArray alloc] init];
for (NSDictionary *myDict in MyDictArray){
NSString *tempStr = [myDict objectForKey:#"firstLetter"];
if(currentStr = nil && [currentStr isEqualToString:""]){
currentStr = tempStr;
if([currentStr isEqualToString:"R"] ){
[R_Array addObject:myDict];
i = 0;
}else{
[S_Array addObject:myDict];
i = 1;
}
}else{
if([currentStr isEqualToString:tempStr]){
(i=0)?[R_Array addObject:myDict]:[S_Array addObject:myDict];
}else{
(i=0)?[R_Array addObject:myDict]:[S_Array addObject:myDict];
}
}
}
Base on your dictionaries. There are only two type, so i just created two array and use if-else for solving the problem. if there are multy values, you can try switch-case to do it.
Lets do this
NSMutaleDictionary * speDict = [[NSMutableDictionary alloc] init];
for(i=0;i<26;i++){
switch (i){
case 0:
[speDict setObject:[NSMutableArray alloc] init] forKey:#"A"];
break;
case 1:
[speDict setObject:[NSMutableArray alloc] init] forKey:#"B"];
break;
Case 2:
[speDict setObject:[NSMutableArray alloc] init] forKey:#"C"];
break;
...........
Case 25:
[speDict setObject:[NSMutableArray alloc] init] forKey:#"Z"];
break;
}
}
for (NSDictionary *myDict in MyDictArray){
NSString *tempStr = [myDict objectForKey:#"firstLetter"];
switch (tempStr)
case A:
[self addToMySpeDictArrayWithObject:myDict andStr:temStr];
break;
case B:
[self addToMySpeDictArrayWithObject:myDict andStr:temStr];
break;
Case C:
[self addToMySpeDictArrayWithObject:myDict andStr:temStr];
break;
...........
Case Z:
[self addToMySpeDictArrayWithObject:myDict andStr:temStr];
break;
}
-(void)addToMySpeDictArrayWithObject:(NSDictionary*)_dict andStr:(NString*)_str
{
NSMutableArray *tempArray = [speDict objectForKey:_str];
[tempArray addObject:_dict];
}
then the speDict is like
A:
//all firstletter is A
myDict
myDict
myDict
B:
//all firstletter is B
myDict
myDict
.......
First of all the sample you've provided is an array of dicts (not a dict as the question notes). Now, the easiest way to query this array is by using an NSPredicate. Something like this perhaps:
NSArray *objects = ...; // The array with dicts
NSString *letter = #"S"; // The letter we want to pull out
NSPredicate *p = [NSPredicate predicateWithFormat:#"firstLetter == %#", letter];
NSArray *s = [objects filteredArrayUsingPredicate:p]; // All the 'S' dicts
If for some reason you need to group all of your objects without having to ask for a specific letter each time, you could try something like this:
// Grab all available firstLetters
NSSet *letters = [NSSet setWithArray:[objects valueForKey:#"firstLetter"]];
for (NSString *letter in letters)
{
NSPredicate *p = [NSPredicate predicateWithFormat:#"firstLetter == %#", letter];
NSArray *x = [objects filteredArrayUsingPredicate:p];
// Do something with 'x'
// For example append it on a mutable array, or set it as the object
// for the key 'letter' on a mutable dict
}
And of course you could further optimize this approach by implementing a method for filtering the array based on a letter. I hope that this makes sense.

How to get response from .clp(CLIPS) file?

I am trying to load .clp file in my iPhone application. For that I am using below code
NSString *filePath = [[NSBundle mainBundle]
pathForResource:#"autodemo" ofType:#"clp"];
environment = CreateEnvironment();
char *clipsFileChar = (char *)[filePath cStringUsingEncoding:NSASCIIStringEncoding];
Load(clipsFileChar);
Reset();
Run(-1);
NSString *evalS = [NSString stringWithFormat:#"(find-all-facts ((?f state-list)) TRUE)"];
char * evalStr = (char *)evalS;
DATA_OBJECT obj;// = {0,-1};
// obj.type = STRING;
// obj.value = evalStr;
int i = Eval(evalStr, &obj);
NSLog(#"%d",i);
now when I run this code Eval(evalStr, &obj) gives me 0 every time.
I am using autodemo.clp file from this link.
So, how to make Eval() command work and how do I get response returned by clp file?
thanks,
below code solved my problem, hope it will help to someone else.. :)
InitializeEnvironment();
Clear();
NSString *filePath = [[NSBundle mainBundle]
pathForResource:#"autodemo" ofType:#"clp"];
char *clipsFileChar = (char *)[filePath cStringUsingEncoding:NSASCIIStringEncoding];
IncrementGCLocks();
Load(clipsFileChar);
Reset();
Run(-1);
DecrementGCLocks();
[self nextUIState];
- (void)nextUIState
{
DATA_OBJECT theDO;
NSString * evalS = #"(find-all-facts ((?f state-list)) TRUE)";
char *evalStr = (char *)[evalS cStringUsingEncoding:NSASCIIStringEncoding];
int j = EnvEval(GetCurrentEnvironment(), evalStr, &theDO);
NSLog(#"j = %d",j);
if(factDict)
{
[factDict release];
factDict = nil;
factDict = [[NSMutableDictionary alloc] init];
}
id value = [self objectForDataObject:&theDO];
NSLog(#"%#",[value description]);
}
-(id) objectForDataObject: (DATA_OBJECT*) arg
{
switch(arg->type)
{
case FACT_ADDRESS:
{
DATA_OBJECT data = { 0 };
struct fact* aFact = (struct fact*) arg->value;
if(EnvGetFactSlot(GetCurrentEnvironment(),aFact,(char*)[#"current" UTF8String],&data))
{
[factDict setObject:[self objectForDataObject: &data] forKey:#"current"];
[factDict retain];
}
return factDict;
}
case SYMBOL:
{
NSString *str = [NSString stringWithUTF8String: ValueToString(arg->value)];
if ([str isEqual: #"nil"]) return nil;
if ([str hasPrefix: #"<<<"] && [str hasSuffix: #">>>"])
{
return [self dataFromSymbolString: str];
}
return str;
}
case STRING:
{
return [NSString stringWithUTF8String: ValueToString(arg->value)];
}
case INTEGER:
{
return [NSNumber numberWithInt: ValueToInteger(arg->value)];
}
case FLOAT:
{
return [NSNumber numberWithDouble: ValueToDouble(arg->value)];
}
case EXTERNAL_ADDRESS:
{
return (id) arg->value;
}
case MULTIFIELD:
{
int i, count = GetpDOLength(arg);
NSMutableArray *args = [NSMutableArray arrayWithCapacity: count];
FIELD_PTR fptr = (FIELD_PTR) GetMFPtr(GetpValue(arg),GetpDOBegin(arg));
for(i = 0; i < count; ++i, ++fptr)
{
DATA_OBJECT dobj;
dobj.type = fptr->type;
dobj.value = fptr->value;
[args addObject: [self objectForDataObject: &dobj]];
}
return args;
}
default:
return nil;
}
}
If you find out any other and better way(ofcourse there is), please let me know. :)

Objective-C: NSDictionary and looping through inner NSDictionaries

I get the following NSDictionary when I parse a JSON response from my server:
(
{
fromUname = Ben;
id = ci2n9awef7tm7e142sx;
message = hi;
read = 1;
subject = hi;
time = 1316513972;
toUname = Jill;
},
{
fromUname = Eamorr;
id = asdf98s14u7tm7e142sx;
message = asdf;
read = 0;
subject = asdf;
time = 1316513322;
toUname = Jack;
}
)
I'm really struggling to extract the two subjects.
Here's what I've coded sofar (incomplete...):
...
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *obj=[parser objectWithString:[request responseString] error:nil];
NSLog(#"%#",obj);
NSLog(#"%d",[obj count]);
for(int i=0;i<[obj count];i++){
NSDictionary *message=[obj objectForKey:];
NSLog(#"%#",[message objectForKey:#"subject"]); //I'm stuck...
}
...
Can anyone give me some efficient way of extracting the subjects?
Many thanks in advance,
Its actually an NSArray of NSDictionaries. So to get the information, loop through the array and get the dictionary:
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *obj = [parser objectWithString:[request responseString] error:nil];
NSLog(#"%# : %d",obj, [obj count]);
for (NSDictionary *dict in obj) {
NSLog(#"%#", [dict objectForKey:#"subject"]);
}

Parse NSURL query property

I have a URL like myApp://action/1?parameter=2&secondparameter=3
With the property query I get following part of my URL
parameter=2&secondparameter=3
Is there any way easy to put this in a NSDictionary or an Array?
Thx a lot
You can use queryItems in URLComponents.
When you get this property’s value, the NSURLComponents class parses the query string and returns an array of NSURLQueryItem objects, each of which represents a single key-value pair, in the order in which they appear in the original query string.
Swift
let url = "http://example.com?param1=value1&param2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)
Alternatively, you can add an extension on URL to make things easier.
extension URL {
var queryParameters: QueryParameters { return QueryParameters(url: self) }
}
class QueryParameters {
let queryItems: [URLQueryItem]
init(url: URL?) {
queryItems = URLComponents(string: url?.absoluteString ?? "")?.queryItems ?? []
print(queryItems)
}
subscript(name: String) -> String? {
return queryItems.first(where: { $0.name == name })?.value
}
}
You can then access the parameter by its name.
let url = "http://example.com?param1=value1&param2=param2"
print(url.queryParameters["param1"])
I had reason to write some extensions for this behavior that might come in handy. First the header:
#import <Foundation/Foundation.h>
#interface NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat;
- (NSString *)stringByEncodingURLFormat;
- (NSMutableDictionary *)dictionaryFromQueryComponents;
#end
#interface NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents;
#end
#interface NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents;
#end
These methods extend NSString, NSURL, and NSDictionary, to allow you to convert to and from query components strings and dictionary objects containing the results.
Now the related .m code:
#import "XQueryComponents.h"
#implementation NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat
{
NSString *result = [self stringByReplacingOccurrencesOfString:#"+" withString:#" "];
result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
- (NSString *)stringByEncodingURLFormat
{
NSString *result = [self stringByReplacingOccurrencesOfString:#" " withString:#"+"];
result = [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
- (NSMutableDictionary *)dictionaryFromQueryComponents
{
NSMutableDictionary *queryComponents = [NSMutableDictionary dictionary];
for(NSString *keyValuePairString in [self componentsSeparatedByString:#"&"])
{
NSArray *keyValuePairArray = [keyValuePairString componentsSeparatedByString:#"="];
if ([keyValuePairArray count] < 2) continue; // Verify that there is at least one key, and at least one value. Ignore extra = signs
NSString *key = [[keyValuePairArray objectAtIndex:0] stringByDecodingURLFormat];
NSString *value = [[keyValuePairArray objectAtIndex:1] stringByDecodingURLFormat];
NSMutableArray *results = [queryComponents objectForKey:key]; // URL spec says that multiple values are allowed per key
if(!results) // First object
{
results = [NSMutableArray arrayWithCapacity:1];
[queryComponents setObject:results forKey:key];
}
[results addObject:value];
}
return queryComponents;
}
#end
#implementation NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents
{
return [[self query] dictionaryFromQueryComponents];
}
#end
#implementation NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents
{
NSString *result = nil;
for(__strong NSString *key in [self allKeys])
{
key = [key stringByEncodingURLFormat];
NSArray *allValues = [self objectForKey:key];
if([allValues isKindOfClass:[NSArray class]])
for(__strong NSString *value in allValues)
{
value = [[value description] stringByEncodingURLFormat];
if(!result)
result = [NSString stringWithFormat:#"%#=%#",key,value];
else
result = [result stringByAppendingFormat:#"&%#=%#",key,value];
}
else {
NSString *value = [[allValues description] stringByEncodingURLFormat];
if(!result)
result = [NSString stringWithFormat:#"%#=%#",key,value];
else
result = [result stringByAppendingFormat:#"&%#=%#",key,value];
}
}
return result;
}
#end
Something like that:
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:#"&"]) {
NSArray *elts = [param componentsSeparatedByString:#"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
}
Note : This is sample code. All error cases are not managed.
Try this ;)!
NSString *query = #"parameter=2&secondparameter=3"; // replace this with [url query];
NSArray *components = [query componentsSeparatedByString:#"&"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
for (NSString *component in components) {
NSArray *subcomponents = [component componentsSeparatedByString:#"="];
[parameters setObject:[[subcomponents objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
forKey:[[subcomponents objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
All previous posts do not do the url encoding properly. I would suggest the following methods:
+(NSString*)concatenateQuery:(NSDictionary*)parameters {
if([parameters count]==0) return nil;
NSMutableString* query = [NSMutableString string];
for(NSString* parameter in [parameters allKeys])
[query appendFormat:#"&%#=%#",[parameter stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet],[[parameters objectForKey:parameter] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
return [[query substringFromIndex:1] copy];
}
+(NSDictionary*)splitQuery:(NSString*)query {
if([query length]==0) return nil;
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
for(NSString* parameter in [query componentsSeparatedByString:#"&"]) {
NSRange range = [parameter rangeOfString:#"="];
if(range.location!=NSNotFound)
[parameters setObject:[[parameter substringFromIndex:range.location+range.length] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter substringToIndex:range.location] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
else [parameters setObject:[[NSString alloc] init] forKey:[parameter stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
return [parameters copy];
}
According to the already very clean answer of Onato I wrote an extension for NSURL in Swift where you can get a query param like this:
e.g. the URL contains the pair param=some_value
let queryItem = url.queryItemForKey("param")
let value = queryItem.value // would get String "someValue"
The extension looks like:
extension NSURL {
var allQueryItems: [NSURLQueryItem] {
get {
let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
let allQueryItems = components.queryItems!
return allQueryItems as [NSURLQueryItem]
}
}
func queryItemForKey(key: String) -> NSURLQueryItem? {
let predicate = NSPredicate(format: "name=%#", key)!
return (allQueryItems as NSArray).filteredArrayUsingPredicate(predicate).first as? NSURLQueryItem
}
}
Here is the extension in swift:
extension NSURL{
func queryParams() -> [String:AnyObject] {
var info : [String:AnyObject] = [String:AnyObject]()
if let queryString = self.query{
for parameter in queryString.componentsSeparatedByString("&"){
let parts = parameter.componentsSeparatedByString("=")
if parts.count > 1{
let key = (parts[0] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let value = (parts[1] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
if key != nil && value != nil{
info[key!] = value
}
}
}
}
return info
}
}
The preferred way to deal with URLs is now NSURLComponents. In particular the queryItems property which returns an NSArray of params.
If you want the params in a NSDictionary, here's a method:
+(NSDictionary<NSString *, NSString *>*)queryParamsFromURL:(NSURL*)url
{
NSURLComponents* urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSMutableDictionary<NSString *, NSString *>* queryParams = [NSMutableDictionary<NSString *, NSString *> new];
for (NSURLQueryItem* queryItem in [urlComponents queryItems])
{
if (queryItem.value == nil)
{
continue;
}
[queryParams setObject:queryItem.value forKey:queryItem.name];
}
return queryParams;
}
Caveat: URLs can have repeated params, but the dictionary will only contain the last value of any duplicated param. If that is undesirable, use the queryItems array directly.
For those using Bolts Framework you can use:
NSDictionary *parameters = [BFURL URLWithURL:yourURL].inputQueryParameters;
Remember to import:
#import <Bolts/BFURL.h>
If you happen to have Facebook SDK in your project, you also have Bolts. Facebook is using this framework as a dependency.
Swift 2.1
Oneliner:
"p1=v1&p2=v2".componentsSeparatedByString("&").map {
$0.componentsSeparatedByString("=")
}.reduce([:]) {
(var dict: [String:String], p) in
dict[p[0]] = p[1]
return dict
}
// ["p1": "v1", "p2": "v2"]
Used as an extension on NSURL:
extension NSURL {
/**
* URL query string as dictionary. Empty dictionary if query string is nil.
*/
public var queryValues : [String:String] {
get {
if let q = self.query {
return q.componentsSeparatedByString("&").map {
$0.componentsSeparatedByString("=")
}.reduce([:]) {
(var dict: [String:String], p) in
dict[p[0]] = p[1]
return dict
}
} else {
return [:]
}
}
}
}
Example:
let url = NSURL(string: "http://example.com?p1=v1&p2=v2")!
let queryDict = url.queryValues
// ["p1": "v1", "p2": "v2"]
Please note, if using OS X 10.10 or iOS 8 (or later), it's probably better to use NSURLComponents and the queryItems property and create the dictionary from the NSURLQueryItems directly.
Here's a NSURLComponents based NSURL extension solution:
extension NSURL {
/// URL query string as a dictionary. Empty dictionary if query string is nil.
public var queryValues : [String:String] {
get {
guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) else {
return [:]
}
guard let queryItems = components.queryItems else {
return [:]
}
var result:[String:String] = [:]
for q in queryItems {
result[q.name] = q.value
}
return result
}
}
}
A footnote to the NSURL extension is that it's actually possible in Swift to give the property the same name as the existing string property—query. I didn't know until I tried it, but the polymorphism in Swift lets you differ only on the return type. So if the extended NSURL property is public var query: [String:String] it works. I didn't use this in the example as I find it a little bit crazy, but it does work ...
I published a simple class doing the job under MIT:
https://github.com/anegmawad/URLQueryToCocoa
With it you can have arrays and objects in the query, which are collected and glued together
For Example
users[0][firstName]=Amin&users[0][lastName]=Negm&name=Devs&users[1][lastName]=Kienle&users[1][firstName]=Christian
will become:
#{
name : #"Devs",
users :
#[
#{
firstName = #"Amin",
lastName = #"Negm"
},
#{
firstName = #"Christian",
lastName = #"Kienle"
}
]
}
You can think of it as a URL query counterpart of NSJSONSerializer.
It looks that you are using it to process incoming data from another iOS application. If so, this is what I use for the same purpose.
Initial call (e.g. in external application):
UIApplication *application = [UIApplication sharedApplication];
NSURL *url = [NSURL URLWithString:#"myApp://action/1?parameter=2&secondparameter=3"];
if ([application canOpenURL:url]) {
[application openURL:url];
NSLog(#"myApp is installed");
} else {
NSLog(#"myApp is not installed");
}
Method to extract QueryString data from NSURL and save as NSDictionary:
-(NSDictionary *) getNSDictionaryFromQueryString:(NSURL *)url {
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
NSRange needle = [url.absoluteString rangeOfString:#"?" options:NSCaseInsensitiveSearch];
NSString *data = nil;
if(needle.location != NSNotFound) {
NSUInteger start = needle.location + 1;
NSUInteger end = [url.absoluteString length] - start;
data = [url.absoluteString substringWithRange:NSMakeRange(start, end)];
}
for (NSString *param in [data componentsSeparatedByString:#"&"]) {
NSArray *keyvalue = [param componentsSeparatedByString:#"="];
if([keyvalue count] == 2){
[result setObject:[keyvalue objectAtIndex:1] forKey:[keyvalue objectAtIndex:0]];
}
}
return result;
}
Usage:
NSDictionary *result = [self getNSDictionaryFromQueryString:url];
This class is a nice solution for url parsing.
.h file
#interface URLParser : NSObject {
NSArray *variables;
}
#property (nonatomic, retain) NSArray *variables;
- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;
#end
.m file
#import "URLParser.h"
#implementation URLParser
#synthesize variables;
- (id) initWithURLString:(NSString *)url{
self = [super init];
if (self != nil) {
NSString *string = url;
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:#"&?"]];
NSString *tempString;
NSMutableArray *vars = [NSMutableArray new];
[scanner scanUpToString:#"?" intoString:nil]; //ignore the beginning of the string and skip to the vars
while ([scanner scanUpToString:#"&" intoString:&tempString]) {
[vars addObject:[tempString copy]];
}
self.variables = vars;
}
return self;
}
- (NSString *)valueForVariable:(NSString *)varName {
for (NSString *var in self.variables) {
if ([var length] > [varName length]+1 && [[var substringWithRange:NSMakeRange(0, [varName length]+1)] isEqualToString:[varName stringByAppendingString:#"="]]) {
NSString *varValue = [var substringFromIndex:[varName length]+1];
return varValue;
}
}
return nil;
}
#end
Hendrik wrote a nice example for extension in this question, however I had to re-write it to not use any objective-c library methods. Using NSArray in swift is not the correct approach.
This is the result, all swift and a bit more safe. The usage example will be less lines of code with Swift 1.2.
public extension NSURL {
/*
Set an array with all the query items
*/
var allQueryItems: [NSURLQueryItem] {
get {
let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
if let allQueryItems = components.queryItems {
return allQueryItems as [NSURLQueryItem]
} else {
return []
}
}
}
/**
Get a query item form the URL query
:param: key The parameter to fetch from the URL query
:returns: `NSURLQueryItem` the query item
*/
public func queryItemForKey(key: String) -> NSURLQueryItem? {
let filteredArray = filter(allQueryItems) { $0.name == key }
if filteredArray.count > 0 {
return filteredArray.first
} else {
return nil
}
}
}
Usage:
let queryItem = url.queryItemForKey("myItem")
Or, more detailed usage:
if let url = NSURL(string: "http://www.domain.com/?myItem=something") {
if let queryItem = url.queryItemForKey("myItem") {
if let value = queryItem.value {
println("The value of 'myItem' is: \(value)")
}
}
}
try this:
-(NSDictionary *)getUrlParameters:(NSString *)url{
NSArray *justParamsArr = [url componentsSeparatedByString:#"?"];
url = [justParamsArr lastObject];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:#"&"]) {
NSArray *elts = [param componentsSeparatedByString:#"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
}
return params;
}
Fairly compact approach:
func stringParamsToDict(query: String) -> [String: String] {
let params = query.components(separatedBy: "&").map {
$0.components(separatedBy: "=")
}.reduce(into: [String: String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
return params
}
Most robust solution if you are using a URL to pass data from the web app to the phone and you want to pass arrays, numbers, strings, ...
JSON encode your object in PHP
header("Location: myAppAction://".urlencode(json_encode($YOUROBJECT)));
And JSON decode the result in iOS
NSData *data = [[[request URL] host] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *packed = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];