MGTwitterEngine and iPhone - iphone

I downloaded MGTwitterEngine and added to my iPhone project. It's connecting and getting statues I can tell from dumping them into an NSLog. But, I can't figure out how how I need to parse the calls so I can add them to a table. They are returned as an NSString and look like this:
{
"created_at" = 2009-07-25 15:28:41 -0500;
favorited = 0;
id = 65;
source = "Twitter";
"source_api_request_type" = 0;
text = "The wolf shirt strikes again!! #sdcc :P http://twitpic.com/blz4b";
truncated = 0;
user = {
"created_at" = "Sat Jul 25 20:34:33 +0000 2009";
description = "Host of Tekzilla on Revision3 and Qore on PSN. Also, a geek.";
"favourites_count" = 0;
"followers_count" = 0;
following = false;
"friends_count" = 0;
id = 5;
location = "San Francisco";
name = "Veronica Belmont";
notifications = false;
"profile_background_tile" = false;
"profile_image_url" = "http://blabnow.com/avatar/Twitter_10350_new_twitter_normal.jpg";
protected = 0;
"screen_name" = Veronica;
"statuses_count" = 2;
"time_zone" = UTC;
url = "http://www.veronicabelmont.com";
"utc_offset" = 0;
};
Anybody used this that can tell me how everyone else uses it in their project?
Thanks

What you are seeing in your console is an NSLog of an NSDictionary and not an NSString. From Matt Gemmell's MGTwitterEngine Readme:
The values sent to these methods are all NSArrays containing an NSDictionary for each status or user or direct message, with sub-dictionaries if necessary (for example, the timeline methods usually return statuses, each of which has a sub-dictionary giving information about the user who posted that status).
So whatever object you passed to your NSLog() statement is actually a dictionary and you can access the fields with a call to:
NSString *createdAtDate = [record valueForKey:#"created_at"];
NSString *source = [record valueForKey:#"source"];
// etc...
Where record is the object. Keep in mind that the user field is a sub-dictionary. You access it this way:
NSDictionary *userDict = [record valueForKey:#"user"];
NSString *name = [userDict valueForKey:#"name"];
NSString *location = [userDict valueForKey:#"location"];
// etc...
You could actually use the NSArray returned in the request as your table view's data source and then just extract the one you need by the index in your -cellForRowAtIndexPath table view delegate.
Best Regards,

For anyone else who might find their way here, here's one way to parce the results. (From a newbie, so don't count of this being the standard or even correct way)
The key (pun intended :D) is to use the dictionary in the appropriate delegate method. Check out Matt Long's example code in another thread on the topic.
To parce something like this:
[myTwitterEngine getSearchResultsForQuery:#"#ironsavior"];
His example is this, in the delegate method:
- (void)searchResultsReceived:(NSArray *)searchResults
forRequest:(NSString *)connectionIdentifier
{
if ([searchResults count] > 0)
{
NSDictionary *result = [searchResults objectAtIndex:0];
NSString *fromUser = [result valueForKey:#"from_user"];
NSString *fromUserID = [result valueForKey#"from_user_id"];
// ...
NSString *text = [result valueForKey#"text"];
NSLog(#"User %#(%#): %#", fromUser, fromUserID, text);
}
}
This would give you a very simple message that goes "User username(userid): message".
I'm not sure what the best way to proceed would be, I'm thinking returning a dictionary or an array that you could use elsewhere in your implementation. Or just return the original array and parse it elsewhere.
Check out that other thread for more info.

Related

Filtering an NSArray from JSON?

I'm trying to implement a searchable tableview in my app, where when someone can search a location and get results. It looks something like this:
I'm getting my source from genomes.com which gives more then just cities, it also has parks, buildings, counties, etc. I want to just show locations which are cities.
The data is a JSON file which is parsed by JSONKit. The whole file comes in (maximum 20 objects) and then the searchable table view shows it. I'm not sure if I should parse the JSON file differently, or if I should make the table view show only the results needed. (Performance in this case is not an issue.). The JSON file gets converted to an NSArray.
Here is part of the array:
{
adminCode1 = MA;
adminCode2 = 027;
adminName1 = Massachusetts;
adminName2 = "Worcester County";
adminName3 = "";
adminName4 = "";
adminName5 = "";
continentCode = NA;
countryCode = US;
countryName = "United States";
elevation = 178;
fcl = A;
fclName = "country, state, region,...";
fcode = ADMD;
fcodeName = "administrative division";
geonameId = 4929431;
lat = "42.2000939";
lng = "-71.8495163";
name = "Town of Auburn";
population = 0;
score = "53.40083694458008";
timezone = {
dstOffset = "-4";
gmtOffset = "-5";
timeZoneId = "America/New_York";
};
toponymName = "Town of Auburn";
},
What I want to do is if the "fcl" (seen in the array) is equal to P, then I want it to show that in the table view. If the "fcl" is some other character, then I don't want it to be seen in the table view. I'm pretty sure that an if statement can do that, but I don't know how to get it so that it filters part of it.
Any help would be appreciated! Thanks
EDIT: As of now, this is the code to search:
- (void)delayedSearch:(NSString*)searchString
{
[self.geoNamesSearch cancel];
[self.geoNamesSearch search:searchString
maxRows:20
startRow:0
language:nil];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
self.searchDisplayController.searchBar.prompt = NSLocalizedStringFromTable(#"ILGEONAMES_SEARCHING", #"ILGeoNames", #"");
[self.searchResults removeAllObjects];
// Delay the search 1 second to minimize outstanding requests
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(delayedSearch:) withObject:searchString afterDelay:0];
return YES;
}
Your question is basically, how do you filter your array from a search bar string? If so, you can detect when the text changes via UISearchBarDelegate and then go through your array copying those objects that contain the string you are looking for, i.e.
This is the delegate method you want: searchBar:textDidChange:.
[filterArray removeAllObjects];
for(int i = 0; i < [normalArray count]; i++){
NSRange textRange;
textRange =[[[[normalArray objectAtIndex:i] objectForKey:#"name"] lowercaseString] rangeOfString:[searchBarString lowercaseString]];
//I wasn't sure which objectForKey: string you were looking for, just replace the one you want to filter.
if(textRange.location != NSNotFound)
{
[filterArray addObject:[normalArray objectAtIndex:i]];
}
}
filterTableView = YES;
[tableView reloadData];
Note the filterTableView bool value, this is so your tableView knows either to load normally or the filtered version you just made. You implement this in:
tableView:numberOfRowsInSection: //For number of rows.
tableView:cellForRowAtIndexPath: //For the content of the cells.
Hope this is what you were looking for.
NSMutableArray* filtered = [[NSMutableArray alloc] autorelease];
for (int i=0;i<[data count];i++)
{
NSDictionary* item=[data objectAtIndex:i];
if (#"P" == [item objectForKey:#"fcl"] )
{
[filtered addObject:item];
}
}
So every time the search field changes, you will compute a new array, and then reload your tableview. The number of rows will be the numbers of rows in your filtered array.
To compute the new array, you can do this (assuming an array of dictionaries):
NSString *searchString; // from the search field
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[origArray count]];
for(NSDictionary *dict in origArray) {
NSString *val = [dict objectForKey:#"fcl"];
if([val length] >= searchString) {
NSString subString = [val substringToIndex:[searchString length]];
if([subString isEqualToString:val]) [array addObject:dict];
}
}
Each cell then will get its values from the new array.
Just put your json in a NSDictionary and simply do something like :
if ([[yourJson objectForKey:#"fcl"] stringValue] == #"A")
//doSomething

Getting value from NSDictionary to NSMutableArray

I have this NSDictionary:
Categorys = {
Category = (
{
categoryDescription = {
text = " Description";
};
categoryIconPath = {
text = " 7.jpg";
};
categoryId = {
text = " 25";
};
categoryName = {
text = " My Photos";
};
categoryStatus = {
text = " ON";
};
publicPrivate = {
text = " Private";
};
userId = {
text = " 2";
};
},
..................
I want to get the categoryName (like here My Photos) into my NSArray.
How can I do this?
I am unable to get it
I have tried this code from http://troybrant.net/blog/2010/09/simple-xml-to-nsdictionary-converter/
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] objectForKey:#"Category"] objectForKey:#"categoryName"] stringForKey:#"text"];
but it gives error
2012-03-27 15:30:35.320 File[1481:903] -[NSCFArray objectForKey:]: unrecognized selector sent to instance 0x1462c0
In Categories, the object referenced by the Category key is an array, you can tell by the fact that its contents are bracketed by parentheses ( ... ) not braces { ... }.
Looking at your code and breaking it down:
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] // This is the outermost dictionary
objectForKey:#"Category"] // This is an NSArray
objectForKey:#"categoryName"] stringForKey:#"text"];
// This breaks NSArray doesn't respond to objectForKey:
Breaking it down a bit, you need:
NSDictionary* categorys = [xmlDictionary objectForKey:#"Categorys"];
NSArray* categoryArray = [categorys objectForKey: #"Category"];
NSDictionary* category = [categoryArray objectAtIndex: i]; // i is the index of the category you want e.g. 0
// etc
The answer is in the error message. One of the objects that you are getting from
NSString *category = [[[[xmlDictionary objectForKey:#"Categorys"] objectForKey:#"Category"] objectForKey:#"categoryName"] stringForKey:#"text"];
Doesn't have the selector objectForKey.
Now you need to break down your code and do some debugging to find out where you are going wrong.
Was getting the same error "[NSCFArray objectForKey:]: unrecognized selector sent to instance"
When I looked at the structure of my plist file that was loaded from the bundle at runtime, it did not seem possible that I was using the wrong selectors. The problem is that when I ran the app, it was not pulling the original plist from the bundle, it was pulling a saved plist from my apps directory. Because the saved plist had an incorrect, and completely different structure than my original plist, I indeed was using the wrong selectors.

Exception while passing float value from array to coordinates

i am getting exception over these lines of code dont know why its giving so ..Help me out ..
reason is:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSArrayI floatValue]: unrecognized selector sent to instance 0x7c57840'
coord.latitude = [[[self.holdingArray objectAtIndex:k]
valueForKey:#"latitude"]floatValue];
coord.longitude = [[[self.holdingArray objectAtIndex:k] valueForKey:#"longitude"]
floatValue];
Edit 1
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[appDelegate.dealerDataGlobalArray removeAllObjects];
NSString *requestString;
requestString = [NSString stringWithFormat:#"http://www.hvinfotech.net/projects/carwash
/search.php?search=%#",textField.text];
NSString *strCOntents1 = [NSString stringWithContentsOfURL:[NSURL
URLWithString:requestString] encoding:NSUTF8StringEncoding error:nil];
NSLog(#"Data: %#",strCOntents1);
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL
URLWithString:requestString]];
DealerDetailParser *parser = [[DealerDetailParser alloc]
initDealerDetailParserXMLParser];
[xmlParser setDelegate:parser];
[xmlParser parse];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NSLog(#"dealerDataGlobalArray...... %#",[appDelegate.dealerDataGlobalArray
description]);
[holdingArray addObject:appDelegate.dealerDataGlobalArray];
NSLog(#"holdingArray...... %#",holdingArray);
flagForHoldingArray = YES;
[self loadOurAnnotations];
[textField resignFirstResponder];
return YES;
}
i have edited so that it can be more clear. I am calling the webservice to get the data corresponding to what is entered in text field. After that i call the method in which previous line o code were posted...
Edit2
it gives like
2012-01-23 00:27:35.475 CarWashApp[2584:11603] (
{
businessdesc = "having a garage and teamof 10members, originaly belongs to mumbai
maharashtra.";
"businessdesc_de" = "having a garage and teamof 10members, originaly belongs to
mumbai maharashtra.";
city = vadodra;
"city_de" = "dadar_de";
companyname = "Maruti Suzki Ltd";
"companyname_de" = "Maruti Suzki Ltd _de";
contactname = "bijju sharma";
"contactname_de" = "bijju sharma_de";
coupondetails = "10% Discount";
"coupondetails_de" = "10% Discount _de";
email = "bsharma#gmail.com";
friday = "Friday: Closed";
happyhour = "10:00 AM to 5:00 PM";
id = 5;
latitude = "22.30731";
logo = "http://www.hvinfotech.net/projects/carwash/upload/images/Sunset.jpg";
longitude = "73.181098";
monday = "Monday: Closed";
payment = Visa;
payments = "";
phone1 = 07932453453;
phone2 = 07934344444;
postcode = JU113;
saturday = "Saturday:9:30 AM - 4:00 PM";
services = "";
state = Groningen;
"state_de" = Groningen;
streetaddress1 = "500 golf Road,";
"streetaddress1_de" = "sectore 5 d_de";
streetaddress2 = "airport Road";
"streetaddress2_de" = "";
sunday = "Sunday:9:30 AM - 4:00 PM";
thursday = "Thursday: Closed";
tuesday = "Tuesday:9:30 AM - 4:00 PM";
website = "http://hvinfotechPvtLtd.com";
wednesday = "Wednesday: Closed";
}
)
2012-01-23 00:27:43.434 CarWashApp[2584:11603] (
"22.30731"
)
i need only above value i.e "22.30731" as coordinate but its showing error if i also write like
coord.latitude = [[self.holdingArray objectAtIndex:k] valueForKey:#"latitude"];
What kind of objects are in self.holdingArray - dictionaries?
Ok, what kind of object is in the dictionary for key "longitude"? It appears that they are arrays.
It is often good practice to break apart these kinds of nested statements, so that you can easily debug.
As #Rayfleck said, the object in the array might be not be the kind of object you're expecting it to be. Use the debugger or NSLog() to find out what object it actually is.
Another possibility, is you might have a memory management bug somewhere. Do you have ARC enabled?
And finally, it could be a thread safety issue. In that case, best of luck finding it! There isn't really any easy way to track down threading issues, except read every line of code and try to find the mistake.
EDIT:
You have this line of code:
[holdingArray addObject:appDelegate.dealerDataGlobalArray];
It looks like you are adding an array object to the holdingArray array. Array objects will return another array when you send valueForKey: to them (lookup valueForKey in the documentation for NSArray...).
Anyway, the array object doesn't respond to floatValue. Only NSString and NSNumber objects respond to floatValue.
You should NSLog() these objects, because I think they are not what you expect them to be:
NSLog(#"%#", [self.holdingArray objectAtIndex:k]);
NSLog(#"%#", [[self.holdingArray objectAtIndex:k] valueForKey:#"latitude"]);

NSXMLParser rss Image urls?

I am parsing rss using nsxmlparser and I would like to get some images from the rss...how can I do that?
so here is an example of the rss
<p><img scr = "IMGURL"></p>
how can I get the "IMGURL"
thanks,
TC
Here is a quick hack - only do this if you know the strings are not going to change position. Otherwise you risk crashing.
//for demo purposes lets say your <p><img scr = "IMGURL"></p> is a string named sourceString
//chop it up into an array like this and grab it from the arrays position
//create an array
NSArray *tempArray = [sourceString componentsSeparatedByString:#"\""];
//what this has done, it has create an array of objects from your source string separated by "
//so in your tempArray you will have two objects
// objectAtIndex 0 will be: <p><img scr = you wont need this so we can ignore it
// objectAtIndex 1 will be the string you want. it will be: IMGURL
// so now you can quickly create a string from it like this
NSString * imgURL = [[tempArray objectAtIndex:1]description];
Its a quick and dirty trick... but it works! So long as the data stays the same format. Your call!
You need to use a xml parser like XQuery. The query would be: /p/img#src
You can use a function such as:
NSString *stringForXQuery(NSXMLNode *node, NSString *query)
{
NSArray *results = [node objectsForXQuery:query constants:nil error:nil];
NSUInteger howManyResults = [results count];
if (howManyResults != 1) {
return nil;
}
return [[results objectAtIndex:0] stringValue];
}
And call it like this:
NSString *imgURL = stringForXQuery([yourxmldocument rootElement], #"/p/img#src");

Writing an NSMutableArray to my documents directory fails

I am attempting to cache a web request. Basically I have an app that uses a facebook user's friend list but I don't want to grab it every single time they log in. Maybe refresh once per month. Caching the friend list in a plist in the documents directory seems to make sense for this functionality. I do this as follows:
- (void)writeToDisk {
NSLog(#"writing cache to disk, where cache = %#", cache);
BOOL res = [cache writeToFile:[FriendCache persistentPath] atomically:YES];
NSLog(#"reading cache from disk immediately after writing, res = %d", res);
NSMutableArray *temp = [[NSMutableArray alloc] initWithContentsOfFile:[FriendCache persistentPath]];
NSLog(#"cache read in = %#", temp);
}
+ (NSString *)persistentPath {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"FriendCache.plist"];
}
These are members of a FriendCache singleton I am using which basically wraps an NSMutableArray. I have verified that the peristentPath method is returning a valid path. As you you can see in the writeToDisk method, I verify there is data in the cache and then I print the result of the write and check if any data could be read back in. There is never data read back in, because the result of the file write is 0.
The output of the cache print is very long, but here is the abbreviated version:
2010-12-28 13:35:23.006 AppName[51607:207] writing cache to disk, where cache = (
{
birthday = "<null>";
name = "Some Name1";
pic = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs1324.snc4/7846385648654.jpg";
"pic_big" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs442.snc4/784365789465746.jpg";
"pic_square" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs1324.snc4/7846357896547.jpg";
sex = female;
status = "<null>";
uid = 892374897165;
},
{
birthday = "<null>";
name = "Some Name2";
pic = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs625.ash1/54636536547_s.jpg";
"pic_big" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs170.ash2/65465656365666_n.jpg";
"pic_square" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs625.ash1/654635656547_q.jpg";
sex = female;
status = "<null>";
uid = 7658436;
},
...
One thing I checked out is when using writeToFile, I must make sure the object I am writing has valid plist objects. I did check this and here is how I construct the cache object:
- (void)request:(FBRequest*)request didLoad:(id)result{
NSMutableArray *friendsInfo = [[[NSMutableArray alloc] init] autorelease];
for (NSDictionary *info in result) {
NSString *friend_id = [NSString stringWithString:[[info objectForKey:#"uid"] stringValue]];
NSString *friend_name = nil;
NSString *friend_sex = nil;
NSString *friend_relationship_status = nil;
NSString *friend_current_location = nil;
if ([info objectForKey:#"name"] != [NSNull null]) {
friend_name = [NSString stringWithString:[info objectForKey:#"name"]];
}
if ([info objectForKey:#"relationship_status"] != [NSNull null]) {
friend_relationship_status = [NSString stringWithString:[info objectForKey:#"relationship_status"]];
}
if ([info objectForKey:#"sex"] != [NSNull null]) {
friend_sex = [NSString stringWithString:[info objectForKey:#"sex"]];
}
if ([info objectForKey:#"current_location"] != [NSNull null]) {
friend_current_location = [[info objectForKey:#"current_location"] objectForKey:#"name"];
}
NSString *friend_pic_square = [info objectForKey:#"pic_square"];
NSString *friend_status = [info objectForKey:#"status"];
NSString *friend_pic = [info objectForKey:#"pic"];
NSString *friend_pic_big = [info objectForKey:#"pic_big"];
NSString *friend_birthday = [info objectForKey:#"birthday"];
NSDictionary *friend_info = [NSDictionary dictionaryWithObjectsAndKeys:
friend_id,#"uid",
friend_name, #"name",
friend_pic_square, #"pic_square",
friend_status, #"status",
friend_sex, #"sex",
friend_pic, #"pic",
friend_pic_big, #"pic_big",
friend_birthday, #"birthday",
friend_relationship_status, #"relationship_status",
friend_current_location, #"current_location",
nil];
// If the friend qualifies as a single of your gender, add to the friend cache
if ( [AppHelpers friendQualifies:friend_info] == YES) {
[[FriendCache sharedInstance] push:friend_info];
}
}
[[FriendCache sharedInstance] writeToDisk];
}
My push method just wraps the NSMutableArray push:
- (void)push:(id)o {
[cache addObject:o];
}
Can you think of any reason why the write would fail?
Thanks!
So as we already pointed out, it's because of the usage of the NSNull objects.
The best way to avoid this is to create an object Friend, with all of the needed properties. Then you can easily set nil values, something not possible with NSDictionary objects (well, you'd have to remove the key, which is not very good practice).
Then, by implementing the NSCoding protocol, you can easily archive (serialize) your custom object.
This is a much better way of handling your data, and it will become MUCH easier in the future. You'll be able to call messages on the Friend objects, something not possible with NSDictionary.
Use NSError-aware API for NSPropertyListSerialization to get the data and the NSData NSError aware write API so you get a meaningful error helping you understand what your problem might be.