NSArray compontentsSeperatedByString returns unrecognized selector - iphone

I have a string containing three words, seperated by a pipeline ( | )
I want to break these up into three separate strings
NSArray *bits = [word.variants componentsSeparatedByString: #"|"];
NSLog(#"BITS: %#", bits);
This returns an unrecognized selector. I use this line of code in other areas in my project, and it works fine. But not on this one.
-[__NSArrayI componentsSeparatedByString:]: unrecognized selector sent to instance 0x6dbfa80
Any ideas?

I have also same problem while my string having so many white character , new line character so i cant do anything but finally i got solution as per following:
NSString *artwork = [currentURL valueForKey:#"artwork_large"];
//i got the string artwork ,which is fetch from json.
[smg addObject:artwork];
// add this string to 0th index of an array name:smg
NSSet *setObj1 = [NSSet setWithArray:smg];
//make the nsset for my array (named :smg)
NSString *pictureName = [[setObj1 allObjects] componentsJoinedByString:#","];
//make the string from all the sets joined by ","
picArray = [pictureName componentsSeparatedByString:#","];
//now its time for normal operation means makes the array (name: picArray) from string by componenet separatedbystring method
this way now i got the perfect array which is in our control

You didn't give us the whole error message, but my bet: You are overreleasing either word or variants and therefor the message is received by another object, that doesn't have the method mentioned in the selector. Try NSZombieEnbled. You will find enough post about it on StackOverflow.
edit
The error posted by you fits to my assumption. The only other explanation: variants is a NSArray.

Make it as following, assuming variants as NSString
NSString *lvariant = word.variants;
NSArray *bits = [lvariant componentsSeparatedByString: #"|"];

Related

Getting objectId from parse.com

So I am building an app that uses parse as a backend. I've written my own before but I figured I'll just save some time and use parse. I'm populating a table view with data from parse and that's fine. I want to grab the objectId from an dictionary built from an array from parse.
The output of my array is as follows:
<news_events:pdbIEvOteH:(null)> {\n eventDescription = \"This is a test description.\";\n eventMessage = \"This is a test message.\";\n eventTitle = \"Free Wi-Fi Now Available!\";\n}
The object ID is pdbIEvOteH in the example above. I at first tried getting the id by using:
NSString * objectId = [myDictionary objectForKey:#"objectId"]; But that returned null. I know it is not a problem with my dictionary because I can get other information. The problem is it looks like there is no key for objectId in the array above. As you can see it follows news_events.
I know you can get it with the PFObject but I'm not sure if I can populate a table with a PFObject.
So bottom line is how do I get the objectId.
In my didSelectRowAtIndexPath: method, I did the following:
PFObject *myObject = [parseArray objectAtIndex:indexPath.row];
NSString *objectId = [myObject objectId];
To get the Id of a particular PFObject.
In Swift:
var objectId = object.objectId
In Objective-C:
NSString *objectId = object.objectId;
"So bottom line is how do I get the objectId."
following is answer from 'Joe Blow' with actual/example code for ios.
i had similar issue. retrieving data with following code worked:
NSString *reporterOpinion = [NSString stringWithFormat:#"%#", [object objectForKey:#"reporterOpinion"]];
but trying to do objectForKey with objectId returned null:
NSString *objectId = [NSString stringWithFormat:#"%#", [object objectForKey:#"objectId]];
following 'Joe Blow' answer, the following code returned correct value for objectId:
NSString *objectId = object.objectId;

Why does SBJson JSON parsing only get the last key of interest?

I am using the following JSON: http://www.kb.dk/tekst/mobil/aabningstider_en.json
When I try to parse it by the key "location" as such:
// get response in the form of a utf-8 encoded json string
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
// get most parent node from json string
NSDictionary *json = [jsonString JSONValue];
// get key-path from jason up to the point of json object
NSDictionary *locations = [json objectForKey:#"location"];
NSLog( #"%#", locations );
// iterate through all of the location objects in the json
for (NSDictionary *loc in locations )
{
// pull library name from the json object
NSString *name = [loc valueForKey:#"name"];
// add library data table arrays respectively
[ libraryNames addObject: ( ( name == nil | name.length > 0 ) ? name : #"UnNamed" ) ];
}
When I print the the object locations via NSLog:
{
address = "Universitetsparken 4, 3. etage, 2100 K\U00f8benhavn \U00d8";
desc = "";
lastUpdated = "";
latlng = "55.703124,12.559596";
link = "http://www.farma.ku.dk/index.php?id=3742";
name = "Faculty of Pharmaceutical Sciences Library";
parts = {
part = {
hour = {
day = "5.June Constitution Day (Denmark)";
open = Closed;
};
hours = {
hour = {
day = Friday;
open = "10-16";
};
};
name = main;
};
};
}
Which is only the last value for the "location" keys. Am I doing something wrong?
I tried validating the JSON via http://jsonlint.com/, however when I'd put in the JSON URL as above, it said "valid" - still only the last "locations" key was shown", however if I copy-paste it, it will not validate the JSON, and has to be fixed by removing new-lines from the string.
Also, when i try to parse the JSON and get the "name" fields, I get the following exception:
2012-05-08 15:37:04.941 iPhone App Tabbed[563:f803] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x68bfe70> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
*** First throw call stack:
(0x13dc052 0x156dd0a 0x13dbf11 0x9d2f0e 0x941841 0x940ca9 0x4593 0xf964e 0x114b89 0x1149bd 0x112f8a 0x112e2f 0x1148f4 0x13ddec9 0x365c2 0x3655a 0x25b569 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdbbab 0x25dd1f 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdb2fe 0x5ba30 0x5bc56 0x42384 0x35aa9 0x12c6fa9 0x13b01c5 0x1315022 0x131390a 0x1312db4 0x1312ccb 0x12c5879 0x12c593e 0x33a9b 0x281d 0x2785)
terminate called throwing an exception(lldb)
It would make more sense if the "locations" tag was an array object enclosed by square brackets ([]), however right now it's only an sequence of normal key-value pairs... Sadly, that's the JSON I have to work with.
Please help and thanks a great deal! :)
Sincerely,
Piotr.
The JSON you've got to work with may be valid, but it doesn't make much sense. It has one big dictionary with the location key repeated many times. Most JSON parser will simply return the last value for the repeated key. It would be best if you could change the structure to use an array instead, but if you cannot there's still hope. You can read the stream and stuff the values from the location keys into an array as they come out of it. This is how you'd do that:
#interface BadJsonHelper : NSObject
#property(strong) NSMutableArray *accumulator;
#end
#implementation BadJsonHelper
- (void)parser:(SBJsonStreamParser *)parser foundArray:(NSArray *)array {
// void
}
- (void)parser:(SBJsonStreamParser *)parser foundObject:(NSDictionary *)dict {
[accumulator addObject:dict];
}
#end
You can drop that little helper class at the top of your file, outside the #implementation section of the class where you're doing your work. (There's no need for the #interface and #implementation being in different files.)
In your code, you would use it like this:
BadJsonHelper *helper = [[BadJsonHelper alloc] init];
helper.accumulator = [NSMutableArray array];
SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter new] init];
adapter.delegate = helper;
adapter.levelsToSkip = 1;
SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init];
parser.delegate = adapter;
switch ([parser parse: responseData]) {
case SBJsonStreamParserComplete:
NSLog(#"%#", helper.accumulator);
break;
case SBJsonStreamParserWaitingForData:
NSLog(#"Didn't get all the JSON yet...");
break;
case SBJsonStreamParserError:
NSLog(#"Error: %#", parser.error);
break;
}
This example was originally adapted from the following test:
https://github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m
Update: I created a fully functional example project that loads the JSON asynchronously and parses it. This is available from github.
The JSON is valid, however there is a basic problem regarding the definition of the array of items.
Instead of defining an array of locations using brackets, the JSON redefines the same location key/value pair over and over again. In other words JSON initially says the value of location is the collection with name "The Black Diamond", but immediately after it redefines it with the collection with name "Faculty Library of Humanities" and so on till the last location Faculty of Pharmaceutical Sciences Library".
The same is true for parts and hours.
If you can't fix the result of the JSON and you really need to get it working you may want to modify the JSON removing the "location" keys and adding brackets properly.
Edit
Alternatively you may use an NSScanner and process the JSON result manually. Kinda hacky but it will work as long as the JSON format doesn't change significantly.
Edit
This snipped of code should do the work...
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
int indx = 1;
for (;;)
{
NSRange locationRange = [jsonString rangeOfString:#"\"location\":"];
if (locationRange.location == NSNotFound) break;
jsonString = [jsonString stringByReplacingCharactersInRange:locationRange
withString:[NSString stringWithFormat:#"\"location%d\":", indx++]];
}
NSDictionary *locations = [json objectForKey:#"location"];
As you can see, the result of JSON parsing by SBJson is a NSDictionary. A dictionary contains key/value pairs, and the keys are unique identifiers for the pairs.
The JSON data you need to handle is valid but not a good one. Per RFC 4627 - 2.2:
An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). A name is a string. A single colon comes after each name, separating the name from the value. A single comma separates a value from a following name. The names within an object SHOULD be unique.
Things like jQuery can parse the JSON also, but the result is the same as SBJson (the last one as the one). See Do JSON keys need to be unique?.
It is not a MUST, but it's still not a good practice. It would be much easier if you are able to change the structure of the JSON data on the server side (or even on the client side after receiving it) rather than parsing it as is.

parsing JSON object and sub-elements on iphone app

I am building an app that uses a UPC data base API. I am getting back a JSON object, example from here: http://www.simpleupc.com/api/methods/FetchNutritionFactsByUPC.php
{
"success":true,
"usedExternal":false,
"result"
{
"calories_per_serving":"150",
"cholesterol_per_serving":"15",
"cholesterol_uom":"Mg",
"dvp_calcium":"30",
"dvp_cholesterol":"4",
"dvp_iron":"2",
"dvp_protein":"17",
"dvp_saturated_fat":"8",
"dvp_sodium":"10",
"dvp_total_fat":"4",
"dvp_vitamin_a":"10","
"dvp_vitamin_c":"0",
"dvp_vitamin_d":"25",
"fat_calories_per_serving":"25",
"fiber_per_serving":"<1",
"fiber_uom":"G",
"ingredients":"Fat Free Milk, Milk, Sugar, Cocoa (Processed With Alkali),
Salt, Carrageenan, Vanillin (Artificial Flavor),
Lactase Enzyme, Vitamin A Palmitate And Vitamin D3.",
"protein_per_serving":"8",
"protein_uom":"G",
"size":"240",
"units":"mL",
"servings_per_container":"8",
"sodium_per_serving":"230",
"sodium_uom":"Mg",
"total_fat_per_serving":"2.5",
"total_fat_uom":"G",
"trans_fat_per_serving":"0",
"trans_fat_uom":"G",
"upc":"041383096013"
}
}
My problem is with parsing the "ingredients" element, which is a sub list of the object dictionary.
How would you suggest parsing the ingredients list? If I could get it to an NSArray, assuming commas are separators, that would have been great.
I tried to do this, but looks like its just a string, so no way to parse it.
Any suggestion would be more than welcome. Thanks!
//Thats the whole JSON object
NSDictionary *json_dict = [theResponseString JSONValue];
//Getting "results" which has all the product info
NSArray *myArray = [[NSArray alloc] init];
myArray = [json_dict valueForKey:#"result"];
Now how do I get "ingredients" from myArray in an array form?
You're getting result as an array, but (in JSON terminology) it's not an array. It's an object, so use an NSDictionary. Something like this:1
NSDictionary *result = [json_dict objectForKey:#"result"];
Then you can get the inner ingredients object from that:
NSString *ingredients = [result objectForKey:#"ingredients"];
Edited as per #Bavarious' comment.
1Apologies for glaring errors, as I'm not terribly well-versed in Objective-C. You might need to allocate memory for the returned NSDictionary and NSString pointers; I'm not sure.
Here's all you need to do:
NSDictionary *json_dict = [theResponseString JSONValue];
// Use a key path to access the nested element.
NSArray *myArray = [json_dict valueForKeyPath:#"result.ingredients"];
EDIT
Whoops, Matt's right. Here's how to deal with the string value:
// Use a key path to access the nested element.
NSString *s = [json_dict valueForKeyPath:#"result.ingredients"];
NSArray *ingredients = [s componentsSeparatedByString:#", "];
Note that you might need to trim the '.' character off the last element of the array.

Trouble appendingStrings (NSString) within a for loop

Ok, so i'm pulling a list of addresses for a given postcode from an online datasource. The requests sends me a JSON of an array of arrays, within the first layer of the array are arrays of strings.
These contain for example.
Addressline1, Addressline2, Town, Country, Postcode
I need to add all of these strings together for each address, so that I have just 1 working string for each address. However, sometimes there is a blank field #"" within the arrays.
Here is my for loop.
id object;
NSString *startString = [NSString stringWithString:#"testStart:"];
for (object in arrayContainingAddress) {
NSString *useableString = [NSString stringWithFormat:#"%#", object];
if (![useableString isEqualToString:#""]) {
NSLog(#"%#", useableString);
[startString stringByAppendingString:useableString];
NSLog(#"%#", startString);
}
}
NSLog(#"%#", startString);
The problem is, startString is ALWAYS logging out at the end as 'testStart:', yet useableString logs to contain the correct addressline, town etc, The startString NSLog within the for loop also just logs out as 'testStart:'.
This entire chunk of code is sat inside a while loop which switches the 'arrayContainingAddress' for the appropriate array for each address.
The reason for the 'id object' is that my JSON conversion sometimes converts the values into NSNumbers (the first line of an address might be a house number, e.g. 123) and so I prevent a crash here.
TLDR: The string 'startString' is not being appended throughout my for loop.
stringByAppendingString doesn't do anything to the string it is called on. It returns a new autoreleased string that is the concatenation of the two.
What you want to do is make your startString a mutable string:
NSMutableString *startString = [NSMutableString stringWithString:#"testStart:"];
then use the appendString method:
[startString appendString:useableString];
You should change your code, as follows:
if (![useableString isEqualToString:#""]) {
NSLog(#"%#", useableString);
startString = [startString stringByAppendingString:useableString];
NSLog(#"%#", startString);
}
You were not updating the startString in the loop.

Objective - C Rookie NSString Problem

I have this code:
// Fill out the email body text
NSString *emailBody = (#"Name:%#\nNumber of People:\nDate:", name.text);
NSLog(#"%#", emailBody);
As you can see I'm trying to append name.text to the e-mail body just after "Name:". However, NSLog only outputs the string contained in name.text and none of the rest of the e-mail body. What am I doing wrong here that the code deletes the rest of the string apart from name.text?
E.G if name.text contained the text "Jack", then NSLog would only output "Jack" and not:
Name: Jack
Number of People: x
Date: x
Which is what I am looking for.
Can anyone give me an insight as to what I'm doing wrong?
Thanks,
Jack
Use +stringWithFormat method:
NSString *emailBody = [NSString stringWithFormat:#"Name:%#\nNumber of People:\nDate:", name.text];
What you have now is a valid code, but it doesn't do what you want:
(#"Name:%#\nNumber of People:\nDate:", name.text);
calls a comma operator - it evaluates its 1st parameter, discards it and returns 2nd parameter, so that's why emailBody is eventually filled with name.text value
You should write
NSString *emailBody = [#"Name:%#\nNumber of People:\nDate:" stringByAppendingString:name.text];
Or, if it doesn't compile,
[[NSString stringWithString:#"Name:%#\nNumber of People:\nDate:"] stringByAppendingString:name.text]
generally you want to either use stringWithFormat as was suggested, which creates an autorelease string that follows the format you have, or you can use initWithFormat instead, which creates a string you can manually manage for better memory behavior, if necessary.
some books will insist that for the iphone, which has limited memory, you don't depend on autorelease objects more than it absolutely necessary, so you'd often find this instead:
NSString *emailBody = [[NSString alloc] initWithFormat:#"Name:%#\nNumber of People:\nDate:", name.text];
Then you can use "emailBody" and immediately after you are done with it, put in this line:
[emailBody release];
This is a good habit to get into in general.