How to parse JSON and have 2 final arrays of data - iphone

I am parsing an itunes rss feed with JSON but I have run into a problem. The following code is running properly for one the movieName output but I still don't get the movieSummary output.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
allDataDictionary = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
feed = [allDataDictionary objectForKey:#"feed"];
arrayOfEntry = [feed objectForKey:#"entry"];
for (NSDictionary *dictionTitle in arrayOfEntry) {
NSDictionary *title = [dictionTitle objectForKey:#"title"];
NSString *labelTitle = [title objectForKey:#"label"];
[arrayLable addObject:labelTitle];
NSDictionary *summary = [dictionTitle objectForKey:#"summary"];
NSString *labelSummary = [summary objectForKey:#"label"];
[arraySummary addObject:labelSummary];
}
movieName.text = [arrayLable objectAtIndex:0];
movieSummary.text = [arraySummary objectAtIndex:0]; //This is not displaying
}
Here is the link that I am parsing: http://itunes.apple.com/us/rss/topmovies/limit=300/json

I run into this situation a lot. I use something like this. Replace your code
NSString *labelTitle = [title objectForKey:#"label"];
[arrayLable addObject:labelTitle];
with
NSString * labelTitle = [ [ title objectForKey:#"label" ] ifNullThenNil ] ;
[ arrayLabel addObject:labelTitle ? labelTitle : #"" ] ; // you could also use #"<unknown>" or similar instead of #""
where -ifNullThenNil is provided via category:
#implementation NSObject (IfNullThenNil)
-(id)ifNullThenNil { return self ; }
#end
#implementation NSNull (IfNullThenNil)
-(id)ifNullThenNil { return nil ; }
#end

The problem was that when I was adding the strings to the Array that it sometimes contained NULL's thus the following code helped me out
if ([[arrayName objectAtIndex:0] isKindOfClass:[NSNull class]]) {
labelName.text = #"This is NULL";
} else {
[arrayName addObject:labelName];
}
if ([[arraySummary objectAtIndex:0] isKindOfClass:[NSNull class]]) {
labelSummary.text = #"This is NULL";
} else {
[arraySummary addObject:labelSummary];
}

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

iOS array not being filled completely

I am using these two recursive methods to find the paths of files and directories in a certain folder
- (NSMutableArray *)getFilePathsFromDirectory:(NSString *)directory{
NSMutableArray *contents = [[NSMutableArray alloc] init];
NSArray *arr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directory error:nil];
for (NSString *file in arr) {
BOOL isDir;
[[NSFileManager defaultManager] fileExistsAtPath:[directory stringByAppendingPathComponent:file] isDirectory:&isDir];
if (!isDir) {
[contents addObject:[directory stringByAppendingPathComponent:file]];
}
else{
[contents addObject:[directory stringByAppendingPathComponent:file]];
[contents addObject:[self getFilePathsFromDirectory:[directory stringByAppendingPathComponent:file]]];
}
}
return contents;
}
- (NSString *)getPathForItemNamed:(NSString *)name array:(NSMutableArray *)arr{
NSString *str;
if (name) {
for (NSString *s in arr) {
if ([s isKindOfClass:[NSString class]]) {
if ([[s lastPathComponent] isEqualToString:name]) {
return s;
}
}
}
for (NSMutableArray *aq in arr) {
if ([aq isKindOfClass:[NSMutableArray class]]) {
str = [self getPathForItemNamed:name array:aq];
return str;
}
}
}
return str;
}
but the problem is, after going through a certain amount of subdirectories (3-5), this stops returning any path and returns (null). I feel like this has to do with the array not being filled with all the directories before it returns for some reason. Heres how I call these
NSMutableArray *paths = [self getContentsOfPaths:[self downloadsDir]];
path = [self getPathForItemNamed:[tableView cellForRowAtIndexPath:indexPath].textLabel.text array:paths];
NSLog(#"%#", path);
There are two problems with your getPathForItemNamed: method:
When it cannot find a file by name, it returns a value of an uninitialized variable str. This is undefined behavior - you need to set str to nil upon initialization. In fact, you do not need str at all (see the fix below).
When it discovers its first subdirectory, it assumes that the file that it is looking for must be inside that subdirectory, even if it is not. Whatever the first-level recursive invocation of getPathForItemNamed: returns, becomes the return result of the top-level invocation. This is bad: if the file that you are looking for is in the subtree of the second subdirectory, you are never going to find it!
Here is how you can fix your method:
- (NSString *)getPathForItemNamed:(NSString *)name array:(NSMutableArray *)arr{
if (!name) return nil;
for (NSString *s in arr) {
if ([s isKindOfClass:[NSString class]]) {
if ([[s lastPathComponent] isEqualToString:name]) {
return s;
}
}
}
for (NSMutableArray *aq in arr) {
if ([aq isKindOfClass:[NSMutableArray class]]) {
str = [self getPathForItemNamed:name array:aq];
// Return something only when you find something
if (str) return str;
}
}
return nil; // You do not need str at all.
}

Memory Leak in NSObject+JSONSerializableSupport

while removing the runtime memory leaks in my iPad application , I came across this strange memory leak in NSObject+JSONSerializableSupport class in the following method
+ (id) deserializeJSON:(id)jsonObject {
id result = nil;
if ([jsonObject isKindOfClass:[NSArray class]]) {
//JSON array
result = [NSMutableArray array];
for (id childObject in jsonObject) {
[result addObject:[self deserializeJSON:childObject]];
}
}
else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
//JSON object
//this assumes we are dealing with JSON in the form rails provides:
// {className : { property1 : value, property2 : {class2Name : {property 3 : value }}}}
NSString *objectName = [[(NSDictionary *)jsonObject allKeys] objectAtIndex:0];
Class objectClass = NSClassFromString([objectName toClassName]);
if (objectClass != nil) {
//classname matches, instantiate a new instance of the class and set it as the current parent object
result = [[[objectClass alloc] init] autorelease];
}
NSDictionary *properties = (NSDictionary *)[[(NSDictionary *)jsonObject allValues] objectAtIndex:0];
NSDictionary *objectPropertyNames = [objectClass propertyNamesAndTypes];
for (NSString *property in [properties allKeys]) {
NSString *propertyCamalized = [[self convertProperty:property andClassName:objectName] camelize];
if ([[objectPropertyNames allKeys]containsObject:propertyCamalized]) {
Class propertyClass = [self propertyClass:[objectPropertyNames objectForKey:propertyCamalized]];
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
}
}
}
else {
//JSON value
result = jsonObject;
}
return result;
}
I am getting the memory leak on this line
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
Please suggest a solution or tell me where i am going wrong.

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];

Open specific trail with a url scheme from NSDictionary

I am using TouchJSON to retrieve the JSON response from http://enbr.co.cc/TrailsApp/shops.php. In my app I use this code to handle a url scheme.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
if (!url) {
return NO;
}
NSString *urlString = [url absoluteString];
NSString *urlStringDecoded = [urlString stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSArray *list = [urlStringDecoded componentsSeparatedByString:#"="];
NSString *urlPrefix = [list objectAtIndex:0];
NSString *name = [list objectAtIndex:1];
if ([urlPrefix isEqualToString:#"tridetrails://opentrail?name"]) {
TrailViewController *trailViewController = [[TrailViewController alloc] initWithNibName:#"TrailViewController" bundle:[NSBundle mainBundle]];
trailViewController.trailToGoto = name;
[self.navigationController pushViewController:trailViewController animated:YES];
[trailViewController release];
}
if ([urlPrefix isEqualToString:#"tridetrails://openshop?name"]) {
ShopViewController *shopViewController = [[ShopViewController alloc] initWithNibName:#"ShopViewController" bundle:[NSBundle mainBundle]];
shopViewController.shopToGoto = name;
[self.navigationController pushViewController:shopViewController animated:YES];
[shopViewController release];
}
return YES;
}
How can I push the correct entry from my NSDictionary created from the JSON to the ShopViewController based on the NSString name? Here is my dictionary printed out by NSLog with NSLog(#"%#", myObj);. Thanks in advance.
{
shops = (
{
blurb = "Bootdoctors blurb";
image = bootdoctorslogo;
locations = "Mountain Village";
motto = "Bootdoctors shop motto";
name = Bootdoctors;
},
{
blurb = "Easy Rider blurb";
image = easyriderlogo;
locations = Telluride;
motto = "Easy Rider shop motto";
name = "Easy Rider";
},
{
blurb = "Paragon Ski & Sport blurb";
image = paragonskiandsportlogo;
locations = Telluride;
motto = "Paragon shop motto";
name = "Paragon Ski & Sport";
},
{
blurb = "Telluride Sports blurb";
image = telluridesportslogo;
locations = "Telluride and Mountain Village";
motto = "Telluride Sports shop motto";
name = "Telluride Sports";
}
);
}
You probably need to give a bit more information about what you are trying to do. For instance you don't say how you retrieve the dictionary containing the details of all the shops and how ShopViewController has access to this dictionary. But selecting one shop by name from the dictionary can be done with something like this:
NSDictionary *jsonResponse; // You don't say how the ShopViewController has access to
// the response so let's just assume a local variable here.
NSDictionary *foundShop = nil; // This will be selected shop after the search below
NSArray *shops = [jsonResponse objectForKey:#'shops'];
for (NSDictionary *shop in shops) {
if ([shop objectForKey:#'name'] isEqualToString:self.shopToGoto]) {
foundShop = shop;
break;
}
}
if (foundShop) {
// Do something with the dictionary keys and values in foundShop
}
else {
// Error condition - shop with required name is not present
// Handle error
}
You could use a NSPredicate to select the shop(s) you are looking for:
NSString* shopName = ...;
NSArray* shops = ...; // this is your JSON-produced array of NSDictionary Shop objects
NSPredicate* predicate = [NSPredicate predicateWithFormat: #"name == '%#'", shopName ];
NSArray* matchingShops = [shops filteredArrayUsingPredicate: predicate];
NSDictionary* firstMatchingShop = [matchingShops objectAtIndex: 0];