How to fetch results from CoreData in a segue - ios

I have an iOS app using CoreData and Restkit 0.20. The main view controller is a collection view that is populated by a request to my server using Restkit. The request to the server returns an image and title to be placed in each cell. If a user selects a cell I need to make a second request to the server for the details about the chosen cell.
I have spent the evening attempting to accomplish this in the prepareForSegue method. The problem is I can't seem to figure out how to get the results from the request that takes place in prepareForSegue.
Here is the prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"newsDetail"]) {
// I USE THIS PORTION TO GET DATA THAT IS NEEDED FOR THE GET REQUEST TO THE SERVER AND TO PASS SOME ADDITIONAL ITEMS FROM THE FIRST REQUEST TO THE DETAIL VIEW CONTROLLER
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
NSString *articleID =[object valueForKey:#"gistID"];
NSString *personID = [object valueForKey:#"userID"];
// I USE THIS LINE TO ASSEMBLE THE STRING FOR getObjectsAtPath BELOW
NSString *getArticle =[NSString stringWithFormat:#"/rest/article/getArticleForView?aid=%#&pid=%#&ip=255.255.255.0",articleID,personID];
//HERE I MAKE THE CALL TO THE SERVER USING RESTKIT METHODS
[[RKObjectManager sharedManager] getObjectsAtPath:getArticle parameters:nil success:nil failure:nil];
[[segue destinationViewController] setDetailItem:object];
}
}
Here is how I load it in the detail view controller.
- (void)configureView
{
if (self.detailItem) {
self.newsDetailText.text = [[self.detailItem valueForKey:#"fullArticle"]description];
NSURL *photoURL =[NSURL URLWithString:[self.detailItem valueForKey:#"imageUrl"]];
NSData *photoData = [NSData dataWithContentsOfURL:photoURL];
self.newsDetailImageView.image =[UIImage imageWithData:photoData];
}
}
It will segue properly and the image (which was already persisted from the first request) will be loaded but the text (which was retrieved from the request at segue) will not. In fact the valueForKey fullArticle is null.
It's probably pretty obvious I'm a rookie based on the question. With that in mind code snippets are VERY helpful. Thanks!

Since you are doing an asynchronous request, the data you wanna show are being arrived after the destinationViewController actually appeared. So, you should pass a success block to your request and update your view inside it. Something like this:
UIViewController *viewController = [segue destinationViewController];
[[RKObjectManager sharedManager] getObjectsAtPath:getArticle parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
if (!operation.error) {
viewController.newsDetailText.text = mappingResult.firstObject[#"fullArticle"];
}
} failure:nil];

Related

Pass the Parsed Data to UICollectionView

I'm a junior level developer and I'm stuck at this very simple thing which I'm unable to figure out.
I've a class which is AFHTTPRequestOperationManager extended. Means it's #interface apiClient : AFHTTPRequestOperationManager in this class I've all my code which fetch images from Imgur API and using AFNetworking I've parsed the data upto a level where I start getting only the link of the images.Now the rootViewController which is a UICollectionViewController extended. In it's viewDidLoad I send a call to my APIClient and it start moving from methods to methods and finally give me an NSMutableArray which contains the images link I'll use in the UIImageView of my CollectionViewCell.
The Question is I'm using this to send the final links back to my rootViewController i.e. GalleryCollectionViewController
GalleryCollectionViewController *gcvc = [[GalleryCollectionViewController alloc] init];
[gcvc recieveGalleryImagesLinks:galleryImgLinkArr];
The problem is that in gcvc it calls numberOfItemsInSection before it can get any response from the API. So that means the count goes out zero and hence it is not displaying the data. So how can I get it to get the API call from apiClient class first and then make the viewController. I hope I've clearly stated my problem and if there is a need of sharing any more code I'll do it.
UPDATE
After authorization this methods gets the gallery images:
- (void)galleryAlbum:(NSString *)ID {
NSLog(#"ID is %#", ID);
[self GET:[NSString stringWithFormat:#"%#3/gallery/album/%#", self.baseURL,ID] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.galleryData = (NSDictionary *)responseObject;
galleryDataArr = [[NSMutableArray alloc] init];
galleryDataArr = [self.galleryData valueForKey:#"data"];
galleryImagesArr = [[NSMutableArray alloc] init];
galleryImagesArr = [galleryDataArr valueForKey:#"images"];
galleryImgLinkArr = [[NSMutableArray alloc] init];
galleryImgLinkArr = [galleryImagesArr valueForKey:#"link"];
GalleryCollectionViewController *gcvc = [[GalleryCollectionViewController alloc] init];
[gcvc recieveGalleryImagesLinks:galleryImgLinkArr];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed #2");
}];
}
Here gcvc is receiving the array with images link and then reloading the collection view as:
-(void)recieveGalleryImagesLinks:(NSMutableArray *)array
{
self.imageLinks = [[NSMutableArray alloc] initWithArray:array];
NSLog(#"Array: %#",self.imageLinks);
[self.collectionView reloadData];
}
But it's giving "UICollectionView: must be initialized with a non-nil layout parameter" although the array is not empty.
This line of code below is incorrect:
GalleryCollectionViewController *gcvc = [[GalleryCollectionViewController alloc] init];
[gcvc recieveGalleryImagesLinks:galleryImgLinkArr];
You need to use a blocks or delegate approach here which will pass back the array back to your collectionViewController and reload the collecrionView.
Even your method call from API class seems to be correct for delegate approach but make it a protocal method. This way you need not have to change much. It will be something like below:
[self.delegate recieveGalleryImagesLinks:galleryImgLinkArr
And when are calling API and is waiting for response show activityIndicator for loading data.

UITableView is only populated after I scroll

I have 2 table views in 2 view controllers. When i select a cell from first screen i want to load the second table , but my problem is : the table is empty and is populating after i scroll.
This is how i populate my table :
When cell is selected :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSURL *url = [NSURL URLWithString:#"http://privatereisen.com/"];
ProgViewController *cd = [self.storyboard instantiateViewControllerWithIdentifier:#"progidentifview"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSString *path=[NSString stringWithFormat :#"dok/TV/pays/italie/json/chaine%d.json",indexPath.row];
[httpClient postPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{resultofData = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:nil];
NSString *valuKey=[NSString stringWithFormat :#"chaine%d",indexPath.row];
NSArray *testArray =[resultofData valueForKey:valuKey];
cd.categ=[[NSArray alloc]initWithArray:testArray];
cd.categArray=[[NSArray alloc]initWithArray:self.categoryArray];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[[UIAlertView alloc]initWithTitle:#":( " message:#"Pas d'internet !" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil]show];
// NSLog(#"[HTTPClient Error]: %#", error.localizedDescription);
}];
[self.navigationController pushViewController:cd animated:YES];
[tableView cellForRowAtIndexPath:indexPath].selected = NO;
}
and in ProgViewController :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
cprogCell *cell = [tableView dequeueReusableCellWithIdentifier:#"identifproecell"];
cell.heure.text = [[_categ objectAtIndex:indexPath.row]objectForKey:#"horaire_programme"];
cell.type.text = #"";
cell.desc.text = [[_categ objectAtIndex:indexPath.row]objectForKey:#"nom_programme"];
return cell;
}
You need to reload the UITableView once your asynchronous request has completed. Currently you push the UIViewController onto the stack and then datasource for it gets set later on, so the cell's only get loaded once you scroll.
To fix this, after you set the cd.categ and cd.categArray, make sure you either call [cd.tableView reloadData], or in the setters for categ or categArray call [self.tableView reloadData]
Too late, but I think you are handling the data to be displayed in the wrong place. For a period of time the user experience is to see a blank screen with no data.
It would be better to move all the data into the controller being pushed. So in your select, create the controller and set the data to be fetched as a property. In the ProgViewController, when inside viewDidAppear start the fetch and activate an activity indicator showing that it is busy. On success or failure stop the activity indicator. On success, call self.tableView.reloadData. On failure, raise the alert and pop the controller.
This way everything is being encapsulated and handled in the right place with the UI updating itself as its internal data state changes.
If you do the fetch from within the initial controller, then you should show its busy and only push the new controller once you have a result for it to display IMHO.

How to get an ID from a web service and use it to locate an object

In my app I'm populating a tableview with an array of jobs that I have from a web service. Every job has a specific job ID that I can grab. Now when someone taps a job from the table view, they are brought to the DetailsViewController which displays all the information about that job. So far, my code looks like this to set the job title in the details view controller:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"next"])
{
NSString *theCorrectUrlString = [NSString stringWithFormat:#"%#", theUrlString];
NSURL *url = [NSURL URLWithString:theCorrectUrlString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableArray *jobsCallArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
for (NSDictionary *theJob in jobsCallArray)
{
DetailsViewController *detailVC = (DetailsViewController*)segue.destinationViewController;
NSString *jobPosTitle = theJob[#"position_title"];
[detailVC setJobTitleString:jobPosTitle];
}
}
}
This code however gives me the same job title for every cell. I want to know how I can get the specific job chosen in my table view, and show all of its details on one page.
All help is appreciated, thanks in advance.
Your code doesn't make much sense. In this little fragment you're iterating through the array, and you're calling setJobTitleString for each object, but obviously whatever this setJobTitleString method does, the value is being overwritten and you only see the last value.
for (NSDictionary *theJob in jobsCallArray)
{
DetailsViewController *detailVC = (DetailsViewController*)segue.destinationViewController;
NSString *jobPosTitle = theJob[#"position_title"];
[detailVC setJobTitleString:jobPosTitle];
}
I can't really tell how to solve this because I don't know what you're trying to do. Retrieving data with dataWithContentsOfURL inside prepareForSegue is weird, especially because you're retrieving all data, but apparently you only want to show the details for one item.

How to fetch results from CoreData Entity

This seems like such a simple thing to do but I just can't put it together. I have an iOS app using RestKit 0.20 to populate my CoreData attributes. My main view controller is a collection view which is populated at startup by making a request to the server.
When a user selects a cell I am able to transfer the data contained in that cell to the detail view (an image and a title). But I also need to make another request to the server to obtain all of the information to be shown in the detail viewController. The Entity name is Gist and the Attribute is fullArticle
Here is the segue to the detailViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"newsDetail"]) {
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[[segue destinationViewController] setmanagedObjectContext:managedObjectContext];
[[segue destinationViewController] setDetailItem:object];
}
}
Here is what i use in -(void)viewDidLoad
{
[super viewDidLoad];
NSString *articleID =[self.detailItem valueForKey:#"articleId"];
NSString *personID = [self.detailItem valueForKey:#"userID"];
NSString *getArticle =[NSString stringWithFormat:#"/rest/article/getArticleForView?aid=%#&pid=%#&ip=255.255.255.0",articleID,personID];
[[RKObjectManager sharedManager] getObjectsAtPath:getArticle parameters:nil success:nil failure:nil];
NSURL *photoURL =[NSURL URLWithString:[self.detailItem valueForKey:#"imageUrl"]];
NSData *photoData = [NSData dataWithContentsOfURL:photoURL];
self.newsDetailImageView.image =[UIImage imageWithData:photoData];
//This is where I am stuck, How do you fetch the Attribute "fullArticle" from the request that I just made?
self.newsDetailText.text = //valueForKey:"fullArticle" from article with ID `articleID`
}
I have tried using the code below but the fetchedResultsController is setup for use with a tableView so there is no way I know of to specify without an indexPath
NSManagedObject *detailText = [self.fetchedResultsController objectAtIndex:0];
self.newsDetailText.text = [[detailText valueForKey:#"fullArticle"] description];
When tried this way fullArticle is null presumably because objectAtIndex:0 is not the right way to designate what object I need to fetch.
Please shed some light on this for me! Clearly a rookie question so code snippets really help! Thanks in advance!
Solution with help from Wain
Wain pointed out that the request does not take place immediately. I needed to use the callbacks in the request to acquire the data I was wanting. Restkit provides the mappingResults in a variety of ways. You can read about that here under RKMappingResult.
Here is how I made it work.
//I REPLACE MY SERVER REQUEST WITH THIS
[[RKObjectManager sharedManager] getObjectsAtPath:getArticle parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//HERE IS HOW I SET THE TEXT FIELD IN THE DETAIL VIEW CONTROLLER
self.newsDetailText.text = [[mappingResult.firstObject valueForKey:#"fullArticle"]description];
// ERROR HANDLING
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Operation failed with error: %#", error);
}];
The request you make doesn't get the results from the server immediately. You should implement the callbacks to handle success and error responses and use the supplied mapping result to get the info to display.

Custom UITableViewCell - Asynchronous UIImage for UIImageView

I have created a custom UITableViewCell which is composed by 2 UILabels and a single UIImageView.
Data associated with cells is available with a NSObject class named CellInfo. CellInfo has 2 properties of NSString type and an UIImage property.
When I create a CellInfo instance, inside the initWithData method (CellInfo class), I do the following:
if(self = [super alloc])
{
//initialize strings variables
self.name = aName;
self.descritpion = aDescription;
[self grabImage]
}
return self;
where grabImage (within CellInfo class) using ASIHTTPrequest framework to grab images in asynchronous manner (in the following code NSURL is alaways the same but in reality it changes with data)
- (void)grabImage
{
NSURL *url = [NSURL URLWithString:#"http://myurl.com/img.png"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
NSData *data = [request responseData];
UIImage* img = [[UIImage alloc] initWithData:data];
self.image = img;
[img release];
// Send a notification if image has been downloaded
[[NSNotificationCenter defaultCenter] postNotificationName:#"imageupdated" object:self];
}];
[request setFailedBlock:^{
NSError *error = [request error];
// Set default image to self.image property of CellInfo class
}];
[request startAsynchronous];
}
I have also a UITableViewController that loads data into the custom cell like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do stuff here...
// Configure the cell...
((CustomTableViewCell*)cell).nameOutlet.text = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).name;
((CustomTableViewCell*)cell).descriptionOutlet.text = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).descritpion;
((CustomTableViewCell*)cell).imageViewOutlet.image = ((CellInfo*) [self.infoArray objectAtIndex:indexPath.row]).image;
return cell;
}
In addiction, this UITableViewController observes notification from the CellInfo class because, at start up, images for visible cells are not displayed. This is the method that is called when the notification is captured:
- (void)imageUpdated:(NSNotification *)notif {
CellInfo * cInfo = [notif object];
int row = [self.infoArray indexOfObject:cInfo];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:0];
NSLog(#"Image for row %d updated!", row);
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
The code works well, but I would like to know if I'm doing right or there is a better way to do this.
My doubt is the following: is it correct to save downloaded images within each CellInfo instance or is it possible to follow another way to cache images using, for example, cache policy provided by ASIHTTPRequest?
P.S. grabImage is not called if the image for a specific CellInfo instance has already been downloaded.
I believe that's pretty neat. Instead of that you might subclass UIImageView class and create an initializer like [AsyncUIImageView initWithURL:] and then put that ASIHttpRequest logic inside the view.
After it finishes loading the picture, there could be two ways:
It can call [self setNeedsDisplay] (an UIView method) so image view is redrawn.
You can pass UITableViewCell or UITableView as a delegate to AsyncUIImgeView so that it could tell table view to reload that cell.

Resources