NSURL directory files - objective-c

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

Related

Convert NSData from NSURLSession to JSON

I am aware that there are many similar SO questions that have a similar title to mine. I have checked them out and am still running into this problem.
I am trying to access an API that returns a string that should/could be formatted as JSON.
To retrieve this string as convert the string to JSON I'm using (unsuccessfully) this code:
NSError *error;
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#",responseString);
NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSLog(#"%#",[json objectForKey:#"ID"]);
NSLog(#"Json: %#",json);
}];
[task resume];
The NSLog(#"Response:...) returns a string that when I enter it into this website: http://jsonviewer.stack.hu confirms that the string is valid JSON.
Both NSLog's that are supposed to return a JSON value come back null.
What iv'e tried:
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&error];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&error];
I have also now tried:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSMutableDictionary *jsonObject;
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
NSDictionary *info = jsonObject;
NSLog(#"Json: %#",info);
}];
[task resume];
What am I doing wrong here? How can I get a NSDictionary (JSON) result.
Your main issue:
If you read the error parameter of +JSONObjectWithData:options:error:, it will tell you this:
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start
with array or object and option to allow fragments not set."
UserInfo={NSDebugDescription=JSON text did not start with array or
object and option to allow fragments not set.}
As stated, your answer looks like this: aStringKey = realAndValidJSONSurroundedByCurvyBrackets, which is not a valid JSON.
After discussion in chat, you have contact with the server side, and it should be their responsibility to give proper JSON. Until they fix it, in order to keep working, you can do:
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
str = [str stringByReplacingCharactersInRange:NSMakeRange(0, [#"aStringKey = " length] ) withString:#""];
NSError *jsonError = nil;
NSDictionary *jsonFinal = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&jsonError];
if (jsonError)
{
NSLog(#"Error: %#", jsonError);
}
But remember, that's a "quick hack/fix" and shouldn't be left in final version and remove as soon as possible.
You tried:
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
The #try{}#catch(NSException *exception) shouldn't work since +JSONObjectWithData:options:error: shouldn't throw a NSException in your case, so in theory, there is nothing to catch, and but it may still not work (since there is a NSError).
Of course, since data parameter should be non null, if it's nil, you'll get a NSException (which would log Exception caught data parameter is nil), but that's another issue and doesn't assure you that the parsing went wrong (because of invalid JSON like in our case) if there is no exception.
Try this:
NSMutableDictionary *jsonObject;
NSError *err = nil;
#try
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
}
#catch (NSException *exception)
{
NSLog( #"Exception caught %#",exception);
}
NSDictionary *info = jsonObject;

AFJSONRequestSerializer POST JSON Array

I am trying to post to an API that is expecting a JSON array as input, for example:
[
{
"token": "bd3",
"display_order": 0
}
]
I am unable to get AFJSONRequestSerializer to produce this, my code to create the params :
NSDictionary *campParams = #{#"token" : #"bd3", #"display_order" : #"0" };
NSArray *arr = [NSArray arrayWithObject:campParams];
NSDictionary *final = [NSDictionary dictionaryWithObject:arr forKey:#""];
Which creates the JSON as:
{"":[{"token":"bd55","display_order":"0"}]}
How can I modify my code to get the proper JSON output? I am creating a second dictionary as the AFHTTPRequestOperationManager accepts parameters as a dictionary, not an array.
[manager PUT:url parameters:final success:^(AFHTTPRequestOperation *operation, id responseObject) {
//done
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
Don't add the last dictionary operation, the questions shows that a JSON array is expected..
NSArray * jsonArray = #[ #{ #"token" : #"bd3", #"display_order" : #"0" } ];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonArray options:0 error:&error];
NSLog(#"jsonData as String: %#", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
Output:
jsonData as String: [{"token":"bd3","display_order":"0"}]
There is a possibility that the last argument should be a number, not a string:
NSArray * jsonArray = #[ #{ #"token" : #"bd3", #"display_order" : #0 } ];
producing:
jsonData as String: [{"token":"bd3","display_order":0}]
which better matches your initial JSON example.
Here is an example but not using AFNetworking because I am not conversant in it:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBody:jsonData];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
NSLog(#"Error: %#", connectionError);
}
else {
NSLog(#"data as String: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
}];
Note: By all means use AFNetworking, it is great! I just know the older OSX/iOS methods by heart and I did not have a test project that already had AFNetworking installed.
My solution was to simply change the request serializer, I was not aware that it was not using JSON by default, so I changed it and now it works.

i want to return data inside a block by using AFNetworking

I have a function using AFJSONRequestOperation, and I wish to return the result only after success. Could you point me in the right direction? I'm still a bit clueless with blocks and AFNetworking specifically.
It would have been better if you had posted your code
Using __block you can use variable inside block
__block NSString *msg;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[hud hide:YES];
NSLog(#"Success %#", operation.responseString);
NSDictionary *message = [NSJSONSerialization JSONObjectWithData:[operation.responseString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
NSLog(#"%#",message);
msg = message
ALERT(#"Posted", message[#"message"]);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.responseString);
NSLog(#"%#",error);
}];
[operation start];

Returning NSDictionary from async code block? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
returning UIImage from block
Hi I'm trying to return dictionary of json twitter data so i can use it in my application. How ever it is being called from a async block. I can not save it or return it any thoughts?
-(NSDictionary *)TweetFetcher
{
TWRequest *request = [[TWRequest alloc] initWithURL:
[NSURL URLWithString: #"http://search.twitter.com/search.json?
q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"] parameters:nil
requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse
*urlResponse,
NSError *error)
{
if ([urlResponse statusCode] == 200)
{
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData
options:0 error:&error];
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog([NSString stringWithFormat:#"%#", [internalDict
objectForKey:#"from_user_name"]]);
----> return dict; // i need this dictionary of json twitter data
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
Thnx in advance!
I feel like I've written a ton of this async code lately.
- (void)tweetFetcherWithCompletion:(void(^)(NSDictionary *dict, NSError *error))completion
{
NSURL *URL = [NSURL URLWithString:#"http://search.twitter.com/search.json?q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"];
TWRequest *request = [[TWRequest alloc] initWithURL:URL parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
if (error) {
completion(nil, error);
return;
}
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog(#"%#", [internalDict objectForKey:#"from_user_name"]);
completion(dict, nil);
}
else {
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
completion(nil, error);
}
}];
}
So, instead of calling self.tweetDict = [self TweetFetcher];, you would call it this way.
[self tweetFetcherWithCompletion:^(NSDictionary *dict, NSError *error) {
if (error) {
// Handle Error Somehow
}
self.tweetDict = dict;
// Everything else you need to do with the dictionary.
}];

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.