I'm trying to use AFNetworking 2.0 to get records from a Parse.com backend. I want to get records updated after a certain date. Parse.com documentation states that comparison queries against a date field need to be url encoded in the format:
'where={"createdAt":{"$gte":{"__type":"Date","iso":"2011-08-21T18:02:52.249Z"}}}'
This works perfectly using curl.
In my app, I am using AFNetworking 2.0 to run the query as below. I first set the request and response serializer when initializing the shared client:
+ (CSC_ParseClient *)sharedClient {
static CSC_ParseClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *baseURL = [NSURL URLWithString:#"https://api.parse.com"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
[config setHTTPAdditionalHeaders:#{ #"Accept":#"application/json",
#"Content-type":#"application/json",
#"X-Parse-Application-Id":#"my app ID",
#"X-Parse-REST-API-Key":#"my api key"}];
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:10 * 1024 * 1024
diskCapacity:50 * 1024 * 1024
diskPath:nil];
[config setURLCache:cache];
_sharedClient = [[CSC_ParseClient alloc] initWithBaseURL:baseURL
sessionConfiguration:config];
_sharedClient.responseSerializer = [AFJSONResponseSerializer serializer];
_sharedClient.requestSerializer = [AFJSONRequestSerializer serializer];
});
return _sharedClient;
}
- (NSURLSessionDataTask *)eventsForSalesMeetingID:(NSString *)meetingID sinceDate:(NSDate *)lastUpdate completion:( void (^)(NSArray *results, NSError *error) )completion {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[dateFormatter setTimeZone:gmt];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSString *dateString = [dateFormatter stringFromDate:lastUpdate];
NSLog(#"date = %#", dateString);
NSDictionary *params = #{#"where": #{#"updatedAt": #{#"$gte": #{#"__type":#"Date", #"iso": dateString}}}};
NSURLSessionDataTask *task = [self GET:#"/1/classes/SalesMeetingEvents"
parameters:params
success:^(NSURLSessionDataTask *task, id responseObject) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
NSLog(#"Response = %#", httpResponse);
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(responseObject[#"results"], nil);
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, nil);
});
NSLog(#"Received: %#", responseObject);
NSLog(#"Received HTTP %d", httpResponse.statusCode);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}];
return task;
}
But this yields a 400 error from the server. The url encoded query string returned looks like this after decoding:
where[updatedAt][$gte][__type]=Date&where[updatedAt][$gte][iso]=2014-01-07T23:56:29.274Z
I tried hard-coding the back end of the querystring like this:
NSString *dateQueryString = [NSString stringWithFormat:#"{\"$gte\":{\"__type\":\"Date\",\"iso\":\"%#\"}}", dateString];
NSDictionary *params = #{#"where":#{#"updatedAt":dateQueryString}};
This gets me closer, but still a 400 error; the returned query string from the server looks like this:
where[updatedAt]={"$gte":{"__type":"Date","iso":"2014-01-07T23:56:29.274Z"}}
How do I get the proper query string from AFNetworking? I started out using the ParseSDK, which made this query super easy, but their SDK is way to heavy (30+ MB).
From the parse.com REST documentation here:
The value of the where parameter should be encoded JSON. Thus, if you look at the actual URL requested, it would be JSON-encoded, then URL-encoded
You are so close with your hardcoded string, you just need to URL-encode the whole query. I have had success with the following:
NSString *dateQueryString = [NSString stringWithFormat:#"{\"updatedAt\":{\"$gte\":{\"__type\":\"Date\",\"iso\":\"%#\"}}}", dateString];
NSDictionary *parameters = #{#"where": dateQueryString};
The following uses an NSDictionary to construct the dateQueryString:
NSString *dateQueryString;
NSDictionary *query = #{ #"updatedAt": #{ #"$gte": #{#"__type":#"Date",#"iso":dateString}}};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:query
options:nil
error:&error];
if (!jsonData) {
NSLog(#"Error: %#", [error localizedDescription]);
}
NSString *dateQueryString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSDictionary *parameters = #{#"where": dateQueryString};
For completeness - I am using a shared instance of an AFHTTPSessionManager subclass:
#implementation ParseAPISessionManager
+ (instancetype)sharedSession {
static ParseAPISessionManager *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[ParseAPISessionManager alloc] initWithBaseURL:[NSURL URLWithString:parseAPIBaseURLString]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (self) {
self.requestSerializer = [AFJSONRequestSerializer serializer];
[self.requestSerializer setValue:parseAPIApplicationId forHTTPHeaderField:#"X-Parse-Application-Id"];
[self.requestSerializer setValue:parseRESTAPIKey forHTTPHeaderField:#"X-Parse-REST-API-Key"];
}
return self;
}
And calling it like this:
ParseAPISessionManager *manager = [ParseAPISessionManager sharedSession];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:13];
[comps setMonth:2];
[comps setYear:2014];
[comps setHour:16];
[comps setMinute:0];
NSDate *feb13 = [[NSCalendar currentCalendar] dateFromComponents:comps];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.'999Z'"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSString *feb13str = [dateFormatter stringFromDate: feb13];
NSString *queryStr = [NSString stringWithFormat:#"{\"updatedAt\":{\"$gte\":{\"__type\":\"Date\",\"iso\":\"%#\"}}}", feb13str];
NSDictionary *parameters = #{#"where": queryStr};
[manager GET:#"classes/TestClass" parameters:parameters success:^(NSURLSessionDataTask *operation, id responseObject) {
NSLog(#"%#", responseObject);
} failure:^(NSURLSessionDataTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
Sorry for the long post, hope it helps
Related
Doing a check to get the current time and date from Google. The first option works although not the best way to do this as it's using a depreciated method and waiting for everything to finish with the synchronous method is not good UX.
-(NSDate*)timeAndDateFromWeb{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL URLWithString:#"https://google.co.uk"]];
[request setHTTPMethod:#"GET"];
NSHTTPURLResponse *httpResponse = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&httpResponse error:nil];
NSString *dateString = [[httpResponse allHeaderFields] objectForKey:#"Date"];
DebugLog(#" *** GOOGLE DATE: %# ****",dateString);
if (httpResponse){
hasDataConnection = YES;
}
else{
hasDataConnection = NO;
}
// Convert string to date object
NSDateFormatter *dateformatted = [NSDateFormatter new];
[dateformatted setDateFormat:#"E, d MMM yyyy HH:mm:ss zzz"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en"];
[dateformatted setLocale:locale];
return [dateformatted dateFromString:dateString];
}
Trying to adapt it is almost there although I'm returning nil for my date string: [dateformatted dateFromString:dateString];
NSURL *url = [NSURL URLWithString:#"https://google.co.uk"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSHTTPURLResponse *httpResponse = nil;
NSString *dateString = [[httpResponse allHeaderFields] objectForKey:#"Date"];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
hasDataConnection = NO;
//NSLog(#"\n\n ----> Not connected Error,%#", [error localizedDescription]);
}
else {
//NSLog(#"\n\n -----> connected: %#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
hasDataConnection = YES;
}
}];
// Convert string to date object
NSDateFormatter *dateformatted = [NSDateFormatter new];
[dateformatted setDateFormat:#"E, d MMM yyyy HH:mm:ss zzz"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en"];
[dateformatted setLocale:locale];
DebugLog(#" *** GOOGLE DATE: %# ****",[dateformatted dateFromString:dateString]);
return [dateformatted dateFromString:dateString];
When you're switching from sync to async, can't return value the same way as before.
When you call sendAsynchronousRequest method, it starts a background task, but your thread continues work instantly. That's why both httpResponse and dateString are null.
So instead of that you should change your return type to void, because you can't return result instantly, and add a callback, which will be run when the job is done. And process your formatting in the completion of the task too:
- (void)timeAndDateFromWeb:(void (^)(NSDate *))completion {
NSURL *url = [NSURL URLWithString:#"https://google.co.uk"];
NSURLSessionTask *task = [NSURLSession.sharedSession
dataTaskWithURL:url
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
// hasDataConnection = NO;
//NSLog(#"\n\n ----> Not connected Error,%#", [error localizedDescription]);
}
else if ([response isKindOfClass:NSHTTPURLResponse.class] ) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSString *dateString = [[httpResponse allHeaderFields] objectForKey:#"Date"];
// Convert string to date object
NSDateFormatter *dateformatted = [NSDateFormatter new];
[dateformatted setDateFormat:#"E, d MMM yyyy HH:mm:ss zzz"];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en"];
[dateformatted setLocale:locale];
// DebugLog(#" *** GOOGLE DATE: %# ****",[dateformatted dateFromString:dateString]);
completion([dateformatted dateFromString:dateString]);
//NSLog(#"\n\n -----> connected: %#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
// hasDataConnection = YES;
}
}];
[task resume];
}
And so the result of this method will be returned to the block, same as result of the background task.
Don't forget, that it will be called on the background thread, same as the download task completion block. So if you wanna change some UI you need to move back to the main thread:
[self timeAndDateFromWeb:^(NSDate *date) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", date);
// UI changes
});
}];
Alternatively you can move it to the main thread inside your function, right before returning the result:
NSDate *date = [dateformatted dateFromString:dateString];
dispatch_async(dispatch_get_main_queue(), ^{
completion(date);
});
I have all the AFNetworking Services written in Separate web service classe. upto now i have been doing it fine passing NSDictionary parameters. but now i got a problem when i need to pass NSData file to web service.
Here how im performing web service
NSData *imageData = UIImageJPEGRepresentation(self.profileImg.image, 0.5); // I need to pass this imageData
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss"];
NSString *stringFromDate = [dateFormatter stringFromDate:[NSDate date]];
//Encrypting Password
NSString *passwordString = _password.text;
NSString *passwordMD5 = [passwordString MD5String];
NSDictionary *params = #{#"username": _username.text,
#"password": passwordMD5,
#"email": _email.text,
#"date":stringFromDate};
WebService *serviceObj = [[WebService alloc] init];
serviceObj.delegate = self;
[serviceObj performSelectorInBackground:#selector(doRegister:) withObject:params];
Here how i have written web service
NSMutableDictionary * parameters = [[NSMutableDictionary alloc]initWithDictionary:params];
NSURL *baseURL = [NSURL URLWithString:#"http://www.example.com/register.php"];
AFHTTPSessionManager * manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager POST:#"" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:imageData name:#"image" fileName:#"profile.png" mimeType:#"image/png"];
} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[delegate didReceiveRegisterResponse:responseObject];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
but i dont know how to assign that imagedata now.
I Have done it passing NSData and NSDictionary inside array. like below
[self performSelectorInBackground:#selector(reloadPage:)
withObject:[NSArray arrayWithObjects:pageIndex,firstCase,nil] ];
- (void) reloadPage: (NSArray *) args {
NSString *pageIndex = [args objectAtIndex:0];
NSString *firstCase = [args objectAtIndex:1];
}
Got the answer from this question
I've been having some problems with my AWSS3GetPreSignedURLRequest and AFNetworking's NSMutableURLRequest. I can successfully upload a file with just the Content-Type header. However, if I add the x-amz-acl and the x-amz-server-side-encryption, the upload will fail with 403 - No Permission error. What can I do? Is it an Amazon side problem, using server-side encryption or ACLs isn't allowed with pre-signed URLs, or changing the request will work? I looked pretty deep on the AWS documentation and the iOS SDK reference, but nothing on this. By the way, I'm using AWS iOS SDK v2. Does anyone how how to do this?
NSString *keyName;
NSString *fileContentTypeStr;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM.d.h.mm.ss"];
if([multipleFileType isEqualToString:#"PNG"])
{
fileContentTypeStr = #"image/png";
keyName = [NSString stringWithFormat:#"image_%#.png", [formatter stringFromDate:[NSDate date]]];
}
else if([multipleFileType isEqualToString:#"JPG"])
{
fileContentTypeStr = #"image/jpeg";
keyName = [NSString stringWithFormat:#"image_%#.jpg", [formatter stringFromDate:[NSDate date]]];
}
self.imageUploadURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"image"]];
[imageData writeToURL:self.imageUploadURL atomically:YES];
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = [NSString stringWithFormat:#"BUCKET-NAME/%#", folderObject.objectId];
getPreSignedURLRequest.key = keyName;
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];
getPreSignedURLRequest.contentType = fileContentTypeStr;
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest]
continueWithBlock:^id(BFTask *task) {
if (task.error) {
NSLog(#"Error: %#",task.error);
} else {
NSURL *presignedURL = task.result;
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:presignedURL];
[URLRequest setValue:fileContentTypeStr forHTTPHeaderField:#"Content-Type"];
[URLRequest setValue:#"AES-256" forHTTPHeaderField:#"x-amz-server-side-encryption"];
[URLRequest setValue:#"private" forHTTPHeaderField:#"x-amz-acl"];
URLRequest.HTTPMethod = #"PUT";
URLRequest.HTTPBody = imageData;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSProgress *progress;
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:URLRequest progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if(!error){
NSLog(#"File was successfully uploaded.");
}
}];
[uploadTask resume];
}
return nil;
}];
Note: The code sample there lacks a few things that are on the global headers, and a few other completion blocks + delegates, but I believe everyone will understand the point here.
Anticipated thanks,
As #YosukeMatsuda noticed in comments you can use putObjectAcl: method.
I put ACL for uploaded object on URLSession didCompleteWithError: delegate method like this:
//
self.awss3 = [[AWSS3 alloc] initWithConfiguration:__your_config__];
//
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error)
{
NSLog(#"S3 UploadTask: %# completed with error: %#", task, [error localizedDescription]);
}
else
{
// AWSS3GetPreSignedURLRequest does not contain ACL property, so it has to be set after file was uploaded to bucket
AWSS3PutObjectAclRequest *aclRequest = [AWSS3PutObjectAclRequest new];
aclRequest.bucket = #"your_bucket";
aclRequest.key = #"your_key";
aclRequest.ACL = AWSS3ObjectCannedACLPublicRead;
[[self.awss3 putObjectAcl:aclRequest] continueWithBlock:^id(BFTask *bftask) {
if (bftask.error)
{
NSLog(#"Error putObjectAcl: %#", [bftask.error localizedDescription]);
}
else
{
AWSEndpoint *endpoint = self.awss3.configuration.endpoint;
NSURL *publicReadURL = [[endpoint.URL URLByAppendingPathComponent:backgroundUploadTask.bucket] URLByAppendingPathComponent:backgroundUploadTask.key];
}
return nil;
}];
}
}
We added - setValue:forRequestParameter: to AWSS3GetPreSignedURLRequest. You can use this method to add ACL and encryption headers.
Also, we introduced a new AWSS3TransferUtility for simplifying the background transfer. Refer to our blog post for more details.
I'd like to make a POST call that has both URL parameters and a JSON body:
URL http://example.com/register?apikey=mykey
JSON { "field" : "value"}
How can I use two different serializers at the same time with AFNNetworking? Here's my code with the URL parameters missing:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager POST:#"http://example.com/register" parameters:json success:^(AFHTTPRequestOperation *operation, id responseObject) {
I make a post method
/**
* Services gateway
* Method get response from server
* #parameter -> object: request josn object ,apiName: api endpoint
* #returm -> void
* #compilationHandler -> success: status of api, response: respose from server, error: error handling
*/
+ (void)getDataWithObject:(NSDictionary *)object onAPI:(NSString *)apiName withController:(UIViewController*)controller
:(void(^)(BOOL success,id response,NSError *error))compilationHandler {
controller = controller;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// set request type to json
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// post request to server
[manager POST:apiName parameters:object success:^(AFHTTPRequestOperation *operation, id responseObject) {
// NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseObject
options:0
error:&error];
//NSString *JSONString = [[NSString alloc] initWithBytes:[jsonData bytes] length:[jsonData length] encoding:NSUTF8StringEncoding];
////
// check the status of API
NSDictionary *dict = responseObject;
NSString *statusOfApi = [[NSString alloc]initWithFormat:#"%#"
,[dict objectForKey:#"OK"]];
// IF Status is OK -> 1 so complete the handler
if ([statusOfApi isEqualToString:#"1"] ) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
compilationHandler(TRUE,responseObject,nil);
} else {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *errorMessages = [responseObject objectForKey:#"messages"];
NSString *message = [errorMessages objectAtIndex:0];
[Utilities showAlertViewWithTitle:apiName message:message];
compilationHandler(FALSE,responseObject,nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSString *message = [NSString stringWithFormat:#"%#",[error localizedDescription]];
NSLog(#"Message is %#", message);
NSString *errorMessage = [NSString stringWithFormat:#"%#",[error localizedDescription]];
if (!([message rangeOfString:#"The request timed out."].location == NSNotFound)) {
[Utilities showAlertViewWithTitle:apiName message:errorMessage];
}
compilationHandler(FALSE,errorMessage,nil);
}];
// For internet reachibility check if changes its state
[self checkInternetReachibility:manager];
}
**for Example when we call the Service **
// calling service gateway API
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
"field",#"value",
nil];
[self getDataWithObject:dict onAPI:KGet_Preferences withController:(UIViewController*)controller :^(BOOL success, id response, NSError *error) {
if( success ) {
NSMutableDictionary *data = [[response valueForKey:#"data"] valueForKey:#"preferences"];
compilationHandler(success,data,error);
} else {
compilationHandler(success,nil,error);
}
}];
I believe there is no automatic way of doing it. However, there is a simple way of achieving it manually:
- (NSMutableURLRequest *)someRequestWithBaseURL:(NSString *)baseUrl
method:(NSString *)method
path:(NSString *)path
uriParameters:(NSDictionary *)uriParameters
bodyParameters:(NSDictionary *)bodyParameters
NSURL *url = [NSURL URLWithString:path relativeToURL:[NSURL URLWithString:baseUrl]];
AFHTTPRequestSerializer *httpRequestSerializer = [AFJSONRequestSerializer serializerWithWritingOptions:0]
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithDictionary:bodyParameters];
if ([httpRequestSerializer.HTTPMethodsEncodingParametersInURI containsObject:method]) {
[parameters addEntriesFromDictionary:uriParameters];
} else {
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
// For urlEncodedString, check http://stackoverflow.com/a/718480/856549
urlComponents.percentEncodedQuery = [uriParameters urlEncodedString];
url = [urlComponents URL];
}
NSError *error;
NSURLRequest *request = [httpRequestSerializer requestWithMethod:method
URLString:[url absoluteString]
parameters:parameters
error:&error];
My app parses information from a Rails app using JSON. I'm looking for a way to load the JSON asynchronously, but I'm having trouble getting my code to work with examples I have found because of the complexity of my code. What do I have to do to make my JSON load asynchronously? Thanks.
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *upcomingReleaseURL = [NSURL URLWithString:#"http://obscure-lake-7450.herokuapp.com/upcoming.json"];
NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_price = [upcomingReleaseDictionary objectForKey:#"release_price"];
upcomingRelease.release_colorway = [upcomingReleaseDictionary objectForKey:#"release_colorway"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.thumb = [upcomingReleaseDictionary valueForKeyPath:#"thumb"];
upcomingRelease.images = [upcomingReleaseDictionary objectForKey:#"images"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
}
One simple approach is to use NSURLConnection's convenient class method sendAsynchronousRequest:queue:error.
The following code snippet is an example how to load a JSON from a server, and where the completion handler executes on a background thread which parses the JSON. It also performs all recommended error checking:
NSURL* url = [NSURL URLWithString:#"http://example.com"];
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse* response,
NSData* data,
NSError* error)
{
if (data) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
// check status code and possibly MIME type (which shall start with "application/json"):
NSRange range = [response.MIMEType rangeOfString:#"application/json"];
if (httpResponse.statusCode == 200 /* OK */ && range.length != 0) {
NSError* error;
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (jsonObject) {
dispatch_async(dispatch_get_main_queue(), ^{
// self.model = jsonObject;
NSLog(#"jsonObject: %#", jsonObject);
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error];
NSLog(#"ERROR: %#", error);
});
}
}
else {
// status code indicates error, or didn't receive type of data requested
NSString* desc = [[NSString alloc] initWithFormat:#"HTTP Request failed with status code: %d (%#)",
(int)(httpResponse.statusCode),
[NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]];
NSError* error = [NSError errorWithDomain:#"HTTP Request"
code:-1000
userInfo:#{NSLocalizedDescriptionKey: desc}];
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error]; // execute on main thread!
NSLog(#"ERROR: %#", error);
});
}
}
else {
// request failed - error contains info about the failure
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error]; // execute on main thread!
NSLog(#"ERROR: %#", error);
});
}
}];
Although, it appears somewhat elaborate, IMO this is a minimalistic and still naïve approach. Among other disadvantages, the main issues are:
it lacks the possibility to cancel the request, and
there is no way to handle more sophisticated authentication.
A more sophisticated approach needs to utilize NSURLConnection delegates. Usually, third party libraries do implement it in this manner, encapsulating the a NSURLConnection request and other relevant state info into a subclass of NSOperation. You may start with your own implementation, for example using this code as a template.
If you just want to get this only json data, you do not need to set up a lot of things.
use the code below. Create jsonParse method which gets a NSData Object.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://obscure-lake-7450.herokuapp.com/upcoming.json"]];
dispatch_sync(dispatch_get_main_queue(), ^{
[self jsonParse:data];
});
});
Download your data async as in this answer: Object-c/iOS :How to use ASynchronous to get a data from URL?
Then run it through the json parser.
To generically run code in a background thread you can use this method:
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Code here is run on a background thread
dispatch_async( dispatch_get_main_queue(), ^{
// Code here is run on the main thread (the UI thread) after your code above has completed so you can update UI after the JSON call has completed if you need to.
});
});
But remember that Apple does not allow you to update UI elements in a background thread. Also, they do not allow you to spawn more threads from a background thread, it must be done from the main thread.
NSString *urlstr=#"http://itunes.apple.com/in/rss/topsongs/limit=25/json";
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:urlstr]];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse* response,
NSData* data, NSError* error)
{
NSError *myError = nil;
NSDictionary *dic1 = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError];
if (myError ==nil) {
NSDictionary*feed =[dic1 objectForKey:#"feed"];
NSArray*arrayofentry =[feed objectForKey:#"entry"];
for(NSDictionary *dic2 in arrayofentry) {
requestReply=[dic2 objectForKey:#"title"];
[arr1 addObject:requestReply];
}
[self.table reloadData];
}
}];
Try this code:
NSURL * inkURL = [NSURL URLWithString:#"your url"];
NSURLRequest * request = [[NSURLRequest alloc]initWithURL:inkURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * response, NSData * data, NSError * error) {
NSData * jsonData = [NSData dataWithContentsOfURL:inkURL];
NSDictionary * dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
self.inkArray = [dataDictionary objectForKey:#"users"];
}];