Using Objective-c to parse information from JSON url - objective-c

I'm trying to write a piece of code that will make use of reddits JSON format. I intend to visit the url: http://www.reddit.com/r/pics/new/.json, search for the string: "title": " and write everything from there till the next apostrophe " to the log, continuing until all titles have been written to the log.
So far I have this, but I am not getting any log output. Can anybody help me?
- (void)viewDidLoad
{
NSString *redditString = #"http://www.reddit.com/r/pics/new/.json";
NSURL *redditURL = [NSURL URLWithString:redditString];
NSError *error;
NSCharacterSet *commaSet;
NSScanner *theScanner;
NSMutableString *jsonText = [[NSMutableString alloc] init];
NSString *TITLE = #"\"title\": \"";
NSString *postTitle;
commaSet = [NSCharacterSet characterSetWithCharactersInString:#"\""];
theScanner = [NSScanner scannerWithString:jsonText];
[jsonText appendString:[NSString stringWithContentsOfURL:redditURL encoding:NSASCIIStringEncoding error:&error]];
if ([theScanner scanString:TITLE intoString:NULL] && [theScanner scanUpToCharactersFromSet:commaSet intoString:&postTitle] && [theScanner scanString:#"\"" intoString:NULL]) {
NSLog(#"%#", postTitle);
}
}
Oh, and this all builds with no errors, but that's no surprise.
Thanks very much for your help, all tips, corrections, or anything else very much appreciated.

NSScanner is the wrong tool for the job. You'd better use a JSON (de)serializer, such as NSJSONSerialization, instead.
To make your life even easier you can take advantage of AFNetworking, a networking framework which supports JSON requests. Your code would reduce to something like
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://www.reddit.com/r/pics/new/.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *entries = responseObject[#"data"][#"children"];
for (NSDictionary *entry in entries) {
NSLog(#"%#", entry[#"title"]);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];

Related

Can't get HTML code AFNetworking 2.0

I tried to make GET HTTP response. I need to get the html code for the subsequent parsing, but responseObject is nil.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[manager GET:#"http://www.example.com/" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError *error;
HTMLParser *parser = [[HTMLParser alloc] initWithString:responseObject error:&error];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
For get html code we will need to build a custom response serializer to decode the NSData response from the web server into a NSString. We will need to subclass AFHTTPResponseSerializer and implement the following method:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
Why for example you have not use this solution below instead of subcalssing. It does the same thing, but you don't need to create additional files, just for overload one method.
So you can just add encoding your responseObjet in the block for example, and it will work as well. I am using POST in my example but it should work with GET in the same way but without parameters, but idea of the just conversation.
+ (void)makeRequestWithParams:(NSDictionary *)params
success:(OperationCompletionBlock)success
failure:(OperationCompletionBlock)failure
{
NSString *path = #"http://www.example.com/";
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFCompoundResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[manager POST:path parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString* encodedString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"%#", encodedString);
success(nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
failure(nil);
}];
}

NSURL directory files

I have for example : www.example.com/ url.
Inside that URL there are few directories for example www.example.com/dir1/ and inside that there are images when clicked I get www.example.com/dir1/image1.jpg.
My problem is that I need to get all files inside www.example.com/dir1/ , so all images that are inside that directory on web. Based on that names I can get final url (like www.example.com/dir1/image1.jpg) but I need to get all the names of images and dunno how.
Thanks.
If you are seeing the apache directory listing you can parse that html and get all your .jpg files.
http://www.raywenderlich.com/14172/how-to-parse-html-on-ios is a tutorial on how to parse HTML
If this is on your server, you need some mechanism to retrieve the names of the files.
For example, if PHP, this is a script that returns a JSON response of all of the JPG/PNG files:
<?php
header('Content-type: application/json');
$files = scandir('.');
$images = array();
foreach ($files as $file)
{
switch(strtolower(substr(strrchr($file,'.'),1)))
{
case 'png':
case 'jpeg':
case 'jpg': $images[] = $file;
}
}
echo json_encode($images);
?>
You can then use NSURLConnection (or AFNetworking or whatever) to retrieve this and convert the JSON to a NSArray.
For example, using AFNetworking:
NSURL *url = [NSURL URLWithString:#"http://yourwebserver.com/some/path/images.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([responseObject isKindOfClass:[NSArray class]])
[self doSomethingWithImageNames:responseObject];
else
NSLog(#"expected array, received: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"AFHTTPRequestOperation error: %#", error);
}];
[op start];
Or NSURLConnection:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
NSLog(#"sendAsynchronousRequest error: %#", connectionError);
return;
}
NSError *jsonError = nil;
NSArray *imageNames = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
NSLog(#"JSONObjectWithData error: %#", jsonError);
return;
}
[self doSomethingWithImageNames:imageNames];
}];
If you have to rely upon the HTML response, while you generally shouldn't use regular expressions, in this limited use case, you can probably get away with it. In my case, my web server reports links to the files using filename syntax, so I can grab those href tags with something like:
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFHTTPResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (![responseObject isKindOfClass:[NSData class]]) {
NSLog(#"Was expecting `NSData` and got %#", responseObject);
return;
}
NSString *string = [[NSString alloc] initWithData:(NSData *)responseObject encoding:NSUTF8StringEncoding];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"<a\\s[\\s\\S]*?href\\s*?=\\s*?['\"](.*?)['\"][\\s\\S]*?>"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSMutableArray *results = [NSMutableArray array];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[results addObject:[string substringWithRange:[result rangeAtIndex:1]]];
}];
[self doSomethingWithImageNames:results];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"AFHTTPRequestOperation error: %#", error);
}];
[op start];

Using NSJSONSerialization for mac

I'm trying to user NSJSONSerialization in a mac app, but i can't get it working, I'm trying to get it to load UserTimeline, and just show the text of the last tweet, and thats it, but I can't seem to find the proper way to use it. the api i have tried to use :
http://api.twitter.com/1/statuses/user_timeline.json?screen_name=AlexTrott_
And
http://twitter.com/statuses/user_timeline/AlexTrott_.json
but neither of them i've had look, this is how i've been trying to use NSJSONSerialization :
NSString *tweets = [NSURL URLWithString:#"http://twitter.com/statuses/user_timeline/AlexTrott_.json"];
NSData *jsonData = [tweets dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(#"%#", json);
that is just what i've been using to see if it's loaded them, but it's been failing me.
I also tried was going to try this method http://www.raywenderlich.com/5492/working-with-json-in-ios-5 however thats solely for iOS and i don't know how i'd get it to work on Mac.
Any links on how to use NSJSONSerialization for mac would be brilliant, or any help with could would also be a major thanks. I tried getting help on the apple developer forums, but no luck there.
NSURL *tweets = [NSURL URLWithString:#"http://twitter.com/statuses/user_timeline/AlexTrott_.json"];
//NSData *jsonData = [tweets dataUsingEncoding:NSUTF8StringEncoding];
//NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSData *jsonData=[NSData dataWithContentsOfURL:tweets];
NSError *e = nil;
id yourJsonData=[NSJSONSerialization JSONObjectWithData:jsonData options:
NSJSONReadingMutableContainers error:&error];
NSLog("%#",yourJsonData );//you must get a json object here
//let's assume you need to get the key kalled user_name from your JSON Response
NSDictionary *theJson=[yourJsonData objectForKey:#"user_name"];
for(NSMutableArray *usernames in theJson){
// do something with usernames from your object
}
My application also receives his last tweet. You should consider the limits to the server with these requests (350 per hour). If you exceed this limit, there is no nsarray in nsdata it is nsdictionary, which describes the error and the request, which will undoubtedly be taken into account. I did it this way:
NSError* error = nil;
id responseBody = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
if (error) {
[[NSAlert alertWithError:error] runModal];
}
if ([responseBody respondsToSelector:#selector(objectAtIndex:)]) {
else {
if ([responseBody count]) {
NSString* currentStatus = [[responseBody objectAtIndex:0] objectForKey:#"text"]; //now you can do smth with your status))
}
else {
NSLog(#"You have not tweetted yet!");
}
}
}
else if([responseBody respondsToSelector:#selector(objectForKey:)]) {
NSString* error = [responseBody objectForKey:#"error"];
NSLog(#"CURRENT STATUS ERROR => %#", error);
}

AFnetworking downloading multiple files

I'm using this code to loop through an array to download multiple files and write to disk.
-(void)download
{
//set url paths
for (NSString *filename in syncArray)
{
NSString *urlpath = [NSString stringWithFormat:#"http://foo.bar/photos/%#", filename];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlpath]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:filename];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
but the problem is it calls the success block after each file is done, (which it should) but I just need one final call back to reload some data and end a progress HUD.
Any pointers in the right direction would be great.
Maybe someday this will help someone, but I was able to use a workaround that probably has major issues but its okay for my simple usage.
I just deleted each line from the sync array after it was processed then ran my code i needed.
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
[SVProgressHUD showWithStatus:#"Updating Photos"];
[syncArray removeObject:filename];
if (!syncArray || !syncArray.count)
{
NSLog(#"array empty");
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self];
[SVProgressHUD dismissWithSuccess:#"Photos Updated"];
}
You can use AFHTTPClient to enqueueBatchOperations and this has a completionBlock which is called when all operations are finished. Should be exactly what you're looking for.

NSURL with http:// works fine but with ftp:// not

NSString *adress = [NSString stringWithFormat:#"ftp://%#:%##%#/%#x050-pegel.txt", benutzer.text, passwort.text, server.text, pfad.text];
NSURL *aURL = [[NSURL alloc]initWithString:adress];
NSString *theData = [NSString stringWithContentsOfURL:aURL];
textview.text = theData;
can some one help me please ?
AFAIK, FTP support is not part of the Cocoa frameworks. You could get more information by using +[NSString stringWithContentsOfURL:encoding:error:] and looking at the NSError object, like so:
NSError *error = nil;
NSString *theData = [NSString stringWithContentsOfURL:aURL encoding:NSUTF8StringEncoding error:&error];
if (error != nil) {
NSLog(#"received error: %#", error);
}
A quick google search for "cocoa ftp" shows that there are several approaches to connecting to an FTP server from Objective-C.