I am making two separate requests to obtain JSON from external sources, I have so far implemented the display of the data from the first request into my table view. My problem is, I need to combine both sets of data into a single table view and sort the data by a common key, which in this case is created_time. I understand I can use some form of array, but how would I go about doing this?
The first:
NSURL *url = [NSURL URLWithString:myURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json) {
self.results = [json valueForKeyPath:#"data"];
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
[operation start];
The second:
NSURL *url = [NSURL URLWithString:#"https://api.twitter.com/1.1/search/tweets.json"];
NSDictionary *parameters = #{#"count" : RESULTS_PERPAGE,
#"q" : encodedQuery};
SLRequest *slRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodGET
URL:url
parameters:parameters];
NSArray *accounts = [self.accountStore accountsWithAccountType:accountType];
slRequest.account = [accounts lastObject];
NSURLRequest *request = [slRequest preparedURLRequest];
dispatch_async(dispatch_get_main_queue(), ^{
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
});
To combine the data from your external sources, you'll want to do the following for each response you get back.
Also, for the sake of the example, I'm assuming the objects you'll be dealing with are all dictionaries. If they aren't, you'll want to add some logic in the comparison block to get at the created_time value depending upon the type of object each one is.
NSArray *data = [json valueForKeyPath: #"data"]; // This is the data from your first example. You'll have to do the same for your second example.
NSMutableArray *allResults = [NSMutableArray arrayWithArray: self.results];
[allResults addObjectsFromArray: data];
[allResults sortUsingComparator: ^NSComparisonResult(id obj1, id obj2) {
NSDictionary *dict1 = obj1;
NSDictionary *dict2 = obj2;
return [[dict1 objectForKey: #"created_time"] compare: [dict2 objectForKey: #"created_time"]];
}];
[self setResults: allResults];
[self.tableView reloadData];
Related
I currentley have a jSON file which is created a by a script which can be seen here: http://ddelay.co.uk/bus/output.json
I currently have managed to setup the following to grab the JSON which works, i just cannot figure how to grab the data to assign it :/
-(void)makeStopRequests{
NSURL *url = [NSURL URLWithString:#"http://ddelay.co.uk/bus/output.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking Asynchronous Task
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id responseObject)
{
NSLog(#"JSON RESULT %#", responseObject);
self.stopArray = [responseObject objectForKey:#"stop_name"];
[self.tableView reloadData];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id responseObject)
{
NSLog(#"Request Failed: %#, %#", error, error.userInfo);
}];
[operation start];
}
I hope somebody can guide me through how I would select the data,
Thank you.
Damien
I would recommend just using something as simple as NSJSONSerialization, which is built into iOS after iOS 5 (may have been earlier, but not too sure).
Just get the data from the request and then parse that into a dictionary with the following code
NSDictionary *dict= [NSJSONSerialization JSONObjectWithData:webData options:NSJSONReadingMutableLeaves error:nil];
get the data using the standard built in networking structure as well
NSURLRequest *someRequest = [[NSURLRequest alloc] initWithURL:someURL cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:10.0];
NSURLConnection *someConnection= [[NSURLConnection alloc] initWithRequest:someRequest delegate:self];
And then do whatever appropriate delegation and data processing. Read the documentation to get more details
You are using AFNetworking and it provides you the response object. That is already being cast to Objective c object.
You can simply assign it to NSArray or NSDictionary like this
NSArray *array = (NSArray*)responseObject;//if your json returns an array
NSDictionay *dict (NSDictionary*)responseObject;//if json returns dictionary.
This url returns JSON in this format;
[ {
"service":"22",
"provider":"First in Yorkshire",
"dest":"Nether Edge to Woodhouse",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Woodhouse"
},
{
"service":"22",
"provider":"First in Yorkshire",
"dest":"Barnsley to Rotherham",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Rotherham"
}
]
Which actually is an array of dictionaries so to access data you have to loop like this.
NSArray *jsonResponse = (NSArray*)responseObject;
for (NSDictionary *dic in jsonResponse){
NSString *service = [dic valueForKey:#"service"];
NSString *provider = [dic valueForKey:#"provider"];
//Same for others
//It will better you create calss with these properties and then add that object to an Array, and on reloading data in table get that object from array in cellForRowAtIndexPath and use requried property to populate your data.
//Or if you just want to use stop name add stop name to array and use it.
NSString *stopName =[dic valueForKey:#"stop_name"];
//[dataArray addObject:stopName]; in case you want to use only stop name
}
Here's minimal code:
self.stopArray = responseObject;
for (NSDictionary *stopDict in self.stopArray) {
NSString *service = [stopDict objectForKey:#"service"];
NSString *provider = [stopDict objectForKey:#"provider"];
NSString *dest = [stopDict objectForKey:#"dest"];
NSString *destURL = [stopDict objectForKey:#"dest URL"];
// do something with this data
}
For anything more complex than this, you'll probably want to make your own data class, so you don't have to parse through dictionaries and arrays in your table view delegate methods.
This question already has answers here:
Can AFNetworking return data synchronously (inside a block)?
(6 answers)
Closed 9 years ago.
I was wondering if I could wait for request to be processed with afnetworking.
Lets say I got this method
- (MWPhoto *)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
//Request goes here, so the method doesn't return anything before it's processed
}
Is that doable?
This would be referred to a synchronous request.
If the method is called on the main thread it will make your app appear to have frozen and is not a suggested way to do networking.
See the dupe question I commented for details on how to do it if you still want to.
You can, but you never want the main queue waiting for some asynchronous operation to complete. If you want something to happen after your asynchronous operation is done, you should use the AFNetworking success block to specify what you want to happen when the operation is done.
So, if you want to provide the caller a pointer to the MWPhoto, rather than having a return type of MWPhoto *, have a return type of void, but supply a completion block so that the caller can handle it when it's done:
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index completion:(void (^)(MWPhoto *))completion
{
if (index < self.images.count) {
GalleryPicture *thumbnail = [images objectAtIndex:index];
NSURLResponse *response = nil;
NSError *error = nil;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", API_URL, #"galleryPicture"]];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:gallery.objectId, #"galleryId", thumbnail.objectId, #"id", [NSNumber numberWithBool:NO], #"thumbnail", nil];
ViveHttpClient *httpClient = [[ViveHttpClient alloc] initWithBaseURL:url];
httpClient.parameterEncoding = AFFormURLParameterEncoding;
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:[url path] parameters:params];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
GalleryPicture *picture = [[GalleryPicture alloc] initWithJSON:JSON];
completion([picture mwPhoto]);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
// handle the error here
}];
// start your operation here
}
}
So, rather than:
MWPhoto *photo = [object photoBrowser:photoBrowser photoAtIndex:index];
// do whatever you want with `photo` here
You might instead do:
[object photoBrowser:photoBrowser photoAtIndex:index completion:^(MWPhoto *photo){
// do whatever you want with `photo` here
}];
Since AFURLConnectionOperation inherits from NSOperation, you can use NSOperation waitUntilFinished method to wait for the operation to end.
However, the success and failure blocks of AFURLConnectionOperation will be executed before waitUntilFinished completes. Nevertheless, you can access the response and error properties of the AFURLConnectionOperation after waitUntilFinished completes.
This is exactly what I did, which reffers to starting synchronyous request
- (MWPhoto *)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
if (index < self.images.count) {
GalleryPicture *thumbnail = [images objectAtIndex:index];
NSURLResponse *response = nil;
NSError *error = nil;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", API_URL, #"galleryPicture"]];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:gallery.objectId, #"galleryId", thumbnail.objectId, #"id", [NSNumber numberWithBool:NO], #"thumbnail", nil];
ViveHttpClient *httpClient = [[ViveHttpClient alloc] initWithBaseURL:url];
httpClient.parameterEncoding = AFFormURLParameterEncoding;
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:[url path] parameters:params];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(!error) {
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
GalleryPicture *picture = [[GalleryPicture alloc] initWithJSON:json];
return [picture mwPhoto];
}
}
return nil;
}
I am showing google street view from my ios app for a perticular location using Google Maps SDK for iOS version: 1.4.0.4450.
It works fine if street view is available.
My question is if street view is not available how to check it?
There is a class GMSPanoramaService. It contains a public member method. I think this can be useful.
- requestPanoramaNearCoordinate:callback:
Retrieves information about a panorama near the given coordinate.
But how to use it?
Thanks in advance!
You may use this rude method
-(void)isStreetViewAvailable:(CLLocationCoordinate2D)location completionBlock: (NWisStreetViewCompletionBlock)completionBlock
{
NSString *loc = [NSString stringWithFormat:#"%.10f,%.10f&", location.latitude, location.longitude];
NWisStreetViewCompletionBlock completeBlock = [completionBlock copy];
NSString *connectionString = [NSString stringWithFormat:#"http://cbk0.google.com/cbk?output=json&ll=%#", loc];
NSLog(#"connect to: %#",connectionString);
NSURL *url = [NSURL URLWithString:connectionString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation;
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//NSLog(#"%#", JSON);
NSLog(#"%#", JSON);
if([JSON objectForKey:#"Location"] == nil)
completeBlock(#"", nil);
//NSLog(#"panoId: %#",[[json objectForKey:#"Location"] objectForKey:#"panoId"]);
completeBlock([[JSON objectForKey:#"Location"] objectForKey:#"panoId"], nil);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:[error description] forKey:NSLocalizedDescriptionKey];
// populate the error object with the details
NSError *err = [NSError errorWithDomain:#"world" code:200 userInfo:details];
completeBlock(NO, err);
}];
[operation start];
}
I have the following code inside a class (static method) which I call to get data from an API. I decided to make this a static method just so I can reuse it on some other parts of the app.
+ (NSArray*) getAllRoomsWithEventId:(NSNumber *)eventId{
NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:#"http://blablba.com/api/Rooms/GetAll/e/%#/r?%#", eventId, [ServiceRequest getAuth]]];
NSMutableArray *rooms = [[NSMutableArray alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Response of getall rooms %#", JSON);
NSArray *jsonResults = (NSArray*)JSON;
for(id item in jsonResults){
Room* room = [[Room alloc]init];
if([item isKindOfClass:[NSDictionary class]]){
room.Id = [item objectForKey:#"Id"];
room.eventId = [item objectForKey:#"EventId"];
room.UINumber = [item objectForKey:#"RoomUIID"];
[rooms addObject:room];
}
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"Error");
}];
[operation start];
[operation waitUntilFinished];
return rooms;
}
Now my issue is, whenever I call this in a ViewController (ViewDidLoad method). The static method will run till the end and will return null on the rooms, but the Nslog will display the "Success" block Nslog a few seconds after. Now I understand that this is asynchronous so it doesn't wait for the success block to execute before it reaches the "return rooms;" line. With all that said, I need some advice as to how to handle this, like maybe a progress bar or something like that? Or something that delays it? I'm not really sure if that's the reight way or if it is, I am not sure how to do it.
Any advice is very much appreciated. Thank you!
AFNetworking is built around asynchronicity—starting a request, and then executing some piece of code once that request has finished.
waitUntilFinished is an anti-pattern, which can block the user interface.
Instead, your method should have no return type (void), and have a completion block parameter that returns the serialized array of rooms:
- (void)allRoomsWithEventId:(NSNumber *)eventId
block:(void (^)(NSArray *rooms))block
{
// ...
}
See the example app in the AFNetworking project for an example of how to do this.
You can write your method following way:
+ (void) getAllRoomsWithEventId:(NSNumber *)eventId:(void(^)(NSArray *roomArray)) block
{
NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:#"http://blablba.com/api/Rooms/GetAll/e/%#/r?%#", eventId, [ServiceRequest getAuth]]];
NSMutableArray *rooms = [[NSMutableArray alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Response of getall rooms %#", JSON);
NSArray *jsonResults = (NSArray*)JSON;
for(id item in jsonResults){
Room* room = [[Room alloc]init];
if([item isKindOfClass:[NSDictionary class]]){
room.Id = [item objectForKey:#"Id"];
room.eventId = [item objectForKey:#"EventId"];
room.UINumber = [item objectForKey:#"RoomUIID"];
[rooms addObject:room];
}
}
block(rooms);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"Error");
block(nil); //or any other error message..
}];
[operation start];
[operation waitUntilFinished];
}
you can call this method like followings:
[MyDataClass getAllRoomsWithEventId:#"eventid1":^(NSArray *roomArray) {
NSLog(#"roomArr == %#",roomArray);
}];
Okay I have another question, tonight, using AFNetworking, i parse my JSON Stream, an add object an MutableArray, when i insert try to print the array outside of the success block, it says null, but inside of this block it works, so how can i pass the _listOfNewsArray into the mainthread ?
This is my code :
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"bgWhitelight" ofType:#"png"];
self.tableView.backgroundColor = [[UIColor alloc] initWithPatternImage:[[UIImage alloc] initWithContentsOfFile:path]];
NSURLRequest *newsRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://aXXXXXXXXXXXipt/beta.php"]];
AFJSONRequestOperation *newsJSONRequest = [AFJSONRequestOperation JSONRequestOperationWithRequest:newsRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
NSArray *newsArray = [JSON objectForKey:#"news"];
_listOfNews = [[NSMutableArray alloc]init];
for (NSDictionary *oneNews in newsArray) {
CCENews *currentNews = [[CCENews alloc]init];
currentNews.title = [oneNews objectForKey:#"title"];
currentNews.content = [oneNews objectForKey:#"content"];
currentNews.category = [currentNews getHiResCategoryPicture:[oneNews objectForKey:#"category"]];
currentNews.date = [oneNews objectForKey:#"date"];
currentNews.imageURL = [oneNews objectForKey:#"pictureurl"];
[_listOfNews addObject:currentNews];
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"%#", error);
}];
[newsJSONRequest start];
In fact, i found the solution, just using self.listOfNews, just had to think about it !-
Move the creation of listOfNews out of the block and into viewDidLoad, or make the ivar a block variable (_block NSM....). I prefer the first solution.