UITableViewCell not showing image downloaded from server - ios

I have table that is a byproduct of “Search Bar and Search Display Controller” for which the UITableViewCell is custom made from xib. Below is the code for the Cell. I am not able to spot why the image is not showing. Will someone please help me troubleshoot? All the other data are showing, except the content of the imageView.
.h
#import <UIKit/UIKit.h>
#interface BCDDogSearchTableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIImageView *dogImageView;
#property (strong, nonatomic) IBOutlet UITextView *dogDescriptionView;
#property(strong,nonatomic) NSString *imageURL;
#end
.m
#import "BCDDogTableViewCell.h"
#import "FICImageCache.h"
#interface BCDDogTableViewCell()
#property(strong,nonatomic) UIImage *image;
#end
#implementation BCDDogTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)awakeFromNib
{
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void)setImageURL:(NSString *)imageURL
{
_imageURL=imageURL;
[self startDownloadingImage];
}
-(void)startDownloadingImage
{
self.image=nil;
if (self.imageURL) {
NSLog(#"The imageURL is %#",self.imageURL);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.imageURL]];
NSURLSessionConfiguration *config=[NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDownloadTask *task =
[session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
if (!error) {
if ([request.URL isEqual:self.imageURL]) {//check in case things have changed somehow
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:localfile]];
dispatch_async(dispatch_get_main_queue(), ^{self.image=image;});//ui thread.
}
}else NSLog(#"loading image error: %#",error);
}];
[task resume];
}
}
-(void)setImage:(UIImage *)image
{
NSLog(#"set image to the image view");
self.dogImageView.image=image;
}
-(UIImage *)image
{
NSLog(#"get image from imageView");
return self.dogImageView.image;
}
-(UIImageView *) dogImageView
{
if (!_dogImageView) _dogImageView=[[UIImageView alloc]init];
return _dogImageView;
}
#end
UPDATE
I added more logging to the following snippet
if (!error) {
NSLog(#"Completion block before if");
if ([request.URL isEqual:self.imageURL]) {//check in case things have changed somehow
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:localfile]];
dispatch_async(dispatch_get_main_queue(), ^{self.image=image;});//ui thread.
}else{
NSLog(#"request url %#",request.URL);
NSLog(#"self url %#", self.imageURL);
}
}else NSLog(#"loading image error: %#",error);
And based on the logging, if ([request.URL isEqual:self.imageURL]) keeps returning false although the two urls printed in the else clause are equal in content.
So I removed that if check and suddenly the image is showing. But I am afraid it's only working because I have one row in my table. So does anyone know why the check is failing while clearly it is the same url inside?

I find the problem, in my if statement I was comparing a url with a string.
if ([request.URL isEqual:self.imageURL])
Thanks to everyone for helping.

Related

Multi watch interface with WatchConnectivity Context

I create an iPhone application (with a view) / Watch (with interface) that displays speed, distance and a timer with a Play / Pause, Stop and Clear.
I used to share WatchConnectivity Application Context data between (Send and receipt of Context). Everything works so far, but I would add another page / interfaces on which exchanges Watch the Context with the iPhone and there I do not know at all how.
My Interface Storyboard
If I turn on the Session 2 interfaces, information is lost
Here is my current code, I want to toggle the display of labels TimeLabel, distanceLabel Label and speed on the second interface
//
// InterfaceController.m
// Watch Extension
//
// Created by Arnaud Roy on 25/10/2015.
// Copyright © 2015 Burotica. All rights reserved.
//
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController() <WCSessionDelegate>
#property (strong, nonatomic) WCSession *session;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceButton *startLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *timeLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *distanceLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *vitesseLabel;
#property (nonatomic,assign) BOOL running;
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
//NSLog(#"SESSION AVAIBLE");
}
//Objective-C
if ([[WCSession defaultSession] isReachable]) {
//NSLog(#"SESSION REACHABLE");
}
self.running = false;
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
- (IBAction)startButton {
if(self.running == false) {
[self sendCmd:#"start"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
self.running = true;
}
else
{
[self sendCmd:#"pause"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
self.running = false;
}
}
- (IBAction)clearButton {
[self sendCmd:#"clear"];
}
- (IBAction)plusmoinsButton {
[self sendCmd:#"plusmoins"];
}
- (IBAction)stopButton {
[self sendCmd:#"stop"];
}
-(void)sendCmd:(NSString*) cmdSendW{
WCSession *session = [WCSession defaultSession];
NSError *error;
[session updateApplicationContext:#{#"cmdSendW":cmdSendW} error:&error];
}
- (void)session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {
NSString *cmdSend = [applicationContext objectForKey:#"cmdSend"];
NSString *timeSend = [applicationContext objectForKey:#"timeSend"];
NSString *distanceSend = [applicationContext objectForKey:#"distanceSend"];
NSString *partielSend = [applicationContext objectForKey:#"partielSend"];
NSString *vitesseSend = [applicationContext objectForKey:#"vitesseSend"];
dispatch_async(dispatch_get_main_queue(), ^{
if([cmdSend isEqual: #"start"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = true;
}
else if([cmdSend isEqual: #"pause"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
else if([cmdSend isEqual: #"stop"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
});
}
#end
Thanks for your help
I'd suggest having your UIApplicationDelegate and ExtensionDelegate create an instance of a new object you make that is the WCSessionDelegate. This object will persist incoming data to disk, and post notifications that the backing data store has been updated. Then each of the UI components can monitor for these notifications and update their UIs as appropriate.

Data from API call not filling inside of method

I decided to go with an embedded API call within a view controller, and I'm having trouble with the data reaching out before the API returns with the information.
How do I wait for the data to be returned before the view controller displays all the values as nulls?
Thanks for any help.
#import "BDChangeApproveController.h"
#import "BDItemChangeDetailAPI.h"
#interface BDChangeApproveController () <NSURLSessionDelegate>
#property (nonatomic, strong) NSURLSession *session;
#property (nonatomic, copy) NSArray *APIItem;
#end
#implementation BDChangeApproveController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)fetchFeedAPIChangeDetail
{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:nil];
NSString *requestString = #"http://URL.com";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithRequest:req
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary *jsonObject1 = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
//NSLog(#"%#", jsonObject1);
self.APIItem = jsonObject1[#"CoDetail"];
NSLog(#"%#", self.APIItem);
}];
[dataTask resume];
}
//API authentication
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLCredential *cred =
[NSURLCredential credentialWithUser:#"demouser"
password:#"secret"
persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFeedAPIChangeDetail];
self.title = #"Action";
self.coNumberLabel.text = self.itemAPI.changeOrder;
//self.recipeImageView.image = [UIImage imageNamed:self.recipe.image];
NSLog(#"testtttt");
NSMutableString *coDetailsText = [NSMutableString string];
coDetailsText =
[[NSMutableString alloc] initWithFormat:#"Review Change Order details bellow\n====================\n%# \n================== \nPlanned Start %#\n==================\nSubcategory: %#\n==================\nService: %#\n==================\nAssociated CIs: %#\n==================\nEnvironment CI: %#\n==================\nApproval Group: %#\n==================\nInitiator : %#\n==================\nCoordinator : %#\n==================\nRisk Level : %#\n==================\nPerforming Group : %#\n==================\nImplementation Plan : %#\n==================\nStatus : %#\n==================\nRecovery Plan : %#\n==================\n",
/*
self.item.title,
self.item.changeOrder,
self.item.subcategory,
self.item.assignmentGroup,
self.item.changeOrder,
self.item.subcategory,
self.item.assignmentGroup,
self.item.approverEid,
self.item.approverEid,
self.item.subcategory,
self.item.assignmentGroup,
self.item.title,
self.item.title,
self.item.title
*/
self.itemAPI.title,
self.itemAPI.plannedStart,
self.itemAPI.subcategory,
self.itemAPI.service,
self.itemAPI.associatedCi,
self.itemAPI.environment,
self.itemAPI.assignmentGroup,
self.itemAPI.initiator,
self.itemAPI.coordinator,
self.itemAPI.riskLevel,
self.itemAPI.performingGroup,
self.itemAPI.implementationPlan,
self.itemAPI.status,
self.itemAPI.recoveryScope
// self.item.valueInDollars,
// self.item.dateCreated,
// self.item.subcategory,
// self.item.service,
// self.item.associatedCIs,
// self.item.environment,
// self.item.approvalGroup,
// self.item.initiator,
// self.item.coordinator,
// self.item.riskLevel,
// self.item.performingGroup,
// self.item.implementationPlan,
// self.item.validationPlan,
//self.item.recoveryScope
];
self.coDetailsTextView.text = coDetailsText;
NSLog(#"overrrr");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// Adding File
#import <Foundation/Foundation.h>
#import "BDItemChangeDetailAPI.h"
#interface BDChangeApproveController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *coNumberLabel;
#property (weak, nonatomic) IBOutlet UITextView *coDetailsTextView;
#property (nonatomic, strong) BDItemChangeDetailAPI *itemAPI;
#end
Looks like you're doing something asynchronously and expecting it to act synchronously.
Look at the below piece of code you're using in viewDidLoad:
[self fetchFeedAPIChangeDetail]; // this takes some time to complete
self.title = #"Action"; // this happens immediately after the above starts, not ends
self.coNumberLabel.text = self.itemAPI.changeOrder; // so does this, so at this point itemAPI is probably nil
fetchFeedAPICheckDetail is an asynchronous process, so it might take a few seconds to complete, whereas setting the title and coNumberLabel happens immediately after, so you don't yet have the itemAPI information from the URL Session. Your code doesn't wait for fetchFeedAPIChangeDetail to be done with the request before continuing onto the next line.
Declare a function
-(void)refreshTextView
{
// set your text view text with the api result here
self.coNumberLabel.text = self.itemAPI.changeOrder;
}
Call this method in at the end of your request block after NSLog(#"%#", self.APIItem);
Like this
[self performSelectorOnMainThread:#selector(refreshTextView) withObject:nil waitUntilDone:NO];
When you will call this method in the block it will be called and will update the textview text with the api results. The block is asynchronous and so when you update your textview later it doesn't gets updated because the block has not yet completed its function and the array is still nil but calling this method in the block would assure that the result gets assigned and updated in the textview.
UPDATE
and you do have a typo here
#property (nonatomic, copy) NSArray *APIItem;
you are naming it with the class name thats a conflict.
Name it something else like #property (nonatomic, copy) NSArray *apiItem; Actually u have a class name APIItem declared because of which a conflict occurs and that is the very reason it is marked blue in xcode. look all class names are marked blue in your code. Like NSArray , NSURLSession etc –

Asynchrone image download with return value required

I'm trying to setup an asynchrone download of images on my application.
I'm using SDWebImage as suggested in this issue.
I put breakpoints on the progress and completed method and everything is normal. It's working perfectly but I have another problem coming from my logic directly. I don't know how to set my image asynchronously on my UIImageView.
Everything is dynamic and each image is called independently
Here is a part of my code:
[myMenu->_userAvatar setImage:[[CacheCache sharedInstance] getUIImageFromPath:currentUser.avatarPhoto.avatarURL]];
Note that CacheCache is my own cache method.
NSURL* myURL=[NSURL URLWithString:path];
//NSData* myData=[NSData dataWithContentsOfURL:myURL];
NSData* myData;
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:myURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
DDLogInfo(#"Downloading...");
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
DDLogInfo(#"Downloaded !");
image = [[UIImage alloc] initWithData:myData];
}
}];
...
return image;
Thank you for your help.
Rather than going for that complex solution, you can try this one. Create NSObject file with name 'DownloadImagesAsynchronously' and replace .h file with following.
#import <Foundation/Foundation.h>
#protocol NotifyParentProtocol <NSObject>
-(void)ImageDownloaded:(BOOL)_isDownloaded;
#end
#interface DownloadImagesAsynchronously : NSObject{
NSMutableData *receivedData;
UIImageView *cCellImageView;
NSURLConnection *imgDownloadConnection;
id<NotifyParentProtocol> __weak delegate;
}
-(void) downloadImageAsynchornously:(NSString *)_imageURL andCellImage:(UIImageView *)_cellImgV;
#property(weak) id<NotifyParentProtocol> delegate;
#end
and replace .m with following code
#import "DownloadImagesAsynchronously.h"
#implementation DownloadImagesAsynchronously
#synthesize delegate;
- (void)loadWithURL:(NSURL *)url{
NSURLConnection *conect = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url]delegate:self];
[conect start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[receivedData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
cCellImageView.image = [UIImage imageWithData:receivedData];
[cCellImageView.layer setCornerRadius:14.0f];
[delegate ImageDownloaded:YES];
}
-(void) downloadImageAsynchornously:(NSString *)_imageURL andCellImage:(UIImageView *)_cellImage{
cCellImageView = _cellImage;
receivedData = [[NSMutableData alloc] init];
NSString *baseURL = #"http://example.com/abc/Gallary";
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#",baseURL,_imageURL]];
[self loadWithURL:url];
}
#end
Now cal on your UIImageView like this, if using TableView then it will lazily download image to each tableview cell.
DownloadImagesAsynchronously *downloadAsynchImageObj = [[DownloadImagesAsynchronously alloc] init];
downloadAsynchImageObj.delegate = self;
[downloadAsynchImageObj downloadImageAsynchornously:model.ImageName1 andCellImage:self.mainImageView];
and implement delegate method. It will notify you when ever image is being downloaded. You can perform your desired action.
- (void)ImageDownloaded:(BOOL)_isDownloaded{
// Image Downloaded.
}
Hope this will work for you, if you have any question related this. Please let me know. Thanks
Thank you guys.
I mixed both of your answers. I simply passed my UIImageView to my method with asynchrone block.
Here is my code:
//view is an UIImageView coming directly from one of the parameters
if (view == nil) {
myData=[NSData dataWithContentsOfURL:myURL];
image = [[UIImage alloc] initWithData:myData];
}
else{
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:myURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
DDLogInfo(#"Downloading...");
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
DDLogInfo(#"Downloaded !");
view.image = image;
}
}];
}
Now I only have a problem about the resize of my picture and its scale but my original issue is fixed.
I hope this will help someone else.

Calling a block from viewdidload

I'm trying to load some JSON data from my model.
But i want to use a block so i can check if there is data with complete YES or NO.
But i call my block from my viewdidload set a breakpoint to check if its called and it comes tot the block but than skips everything between the block.
BlaVIewController.m
#import "MKDataSource.h"
#interface BlaViewController ()
#property (strong, nonatomic) MKDataSource *dataSource;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation BlaViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.dataSource = [[MKDataSource alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.dataSource loadData:^(BOOL complete, NSError *error) {
if (complete) {
//[self.tableView reloadData];
NSLog(#"There is data");
} else {
// error
NSLog(#"No data found");
}
}];
// Do any additional setup after loading the view.
}
And my MKDataSource.m
#interface MKDataSource ()
#property (strong, nonatomic) NSDictionary *data;
#end
#implementation MKDataSource
- (void)loadData:(bool_complete)complete {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
self.data = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];
NSLog(#"JSON Data %#", self.data);
complete(YES, nil);
}
Please explain what i'm doing wrong. I check the JSON and it works. (i removed the JSON url and replaced it with google.com. To protect my data)
Moved the self.dataSource = [[MKDataSource alloc] init]; from initwithnibname to viewdidload.
WORKS

Getting UITableView to populate with data from another class

I am quite new to Objective-C and this is the first time I have attempted to implement MVC. I have a model class where l have an NSArray which will be populated with data from a JSON object. I want to populate my UITableView (in my view controller class), with objects from this array.
Please review my code:
Droplets.h
#interface Droplets : NSObject {
NSArray *dropletsArray;
}
// Get droplets data
- (void) getDropletsList;
//Object initilization
- (id) init;
//Public properties
#property (strong, nonatomic) NSArray *dropletsArray; // Used to store the selected JSON data objects
#end
Droplets.m
#define kBgQueue dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define kDigialOceanApiURL [NSURL URLWithString:#"http://inspiredwd.com/api-test.php"] //Droplets API call
#import "Droplets.h"
#interface Droplets ()
//Private Properties
#property (strong, nonatomic) NSMutableData *data; // Used to store all JSON data objects
#end
#implementation Droplets;
#synthesize dropletsArray;
#synthesize data;
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (void) getDropletsList {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = kDigialOceanApiURL; // Predefined Digital Ocean URL API http request
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self]; //Should be: [[NSURLConnection alloc]initiWithRequest:request delegate:self]; ...however the instance of NSURLConnection is never used, which results in an "entity unsed" error.
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
data = [[NSMutableData alloc]init]; // mutable data dictionary is allocated and initilized
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData {
[data appendData:theData]; // append 'theData' to the mutable data dictionary
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//JSON foundation object returns JSON data from a foundation object. Assigned returned data to a dictionary 'json'.
NSDictionary* jsonData = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions error:0];
self.dropletsArray = [jsonData objectForKey:#"droplets"]; //dictionary of arrays
NSLog(#"Droplets %#", self.dropletsArray);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// If the application is unable to connect to The Digital Ocean Server, then display an UIAlertView
UIAlertView *errorView = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Unable to connect to The Digital Ocean Server, please ensure that you are connected via either WIFI or 3G." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[errorView show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; // Turn of the network activity indicator
}
#end
DropletsList.h
#class Droplets;
#interface DropletsList : UITableViewController
- (Droplets *) modelDroplets;
#end
DropletsList.m
#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
#interface DropletsList ()
//Private properties
#property (strong, nonatomic) Droplets *modelDroplets;
#property (strong, nonatomic) NSArray *tableData;
#end
#implementation DropletsList
#synthesize tableData;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
NSLog(#"get my data from model");
}
return self;
}
- (Droplets *) modelDroplets
{
if (!_modelDroplets) _modelDroplets = [[Droplets alloc]init];
return _modelDroplets;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_modelDroplets = [[Droplets alloc]init];
self.tableData = [_modelDroplets dropletsArray];
[_modelDroplets getDropletsList];
[self.tableView reloadData]; // reload the droplets table controller
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
return 1; // Return the number of sections.
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [_modelDroplets.dropletsArray count]; // Return the number of rows in the section.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// The cell identified by "dropletsList", is assiged as the UITableViewCell
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"dropletsList"];
//NSLog(#"Droplets Name: %#",self.dropletsArray);
// The UITableView text label is assigned the contents from 'dropletsArray', with the object key "name"- name of the droplet
cell.textLabel.text=[[tableData objectAtIndex:indexPath.row]objectForKey:#"name"];
// The UITableView text detail label is assigned the contents from 'dropletsArray', with the object key "status"- status of the droplet
cell.detailTextLabel.text=[[tableData objectAtIndex:indexPath.row]objectForKey:#"status"];
//Evalulate the status of each droplet, setting the colour appropriate to the staus
if ([[[tableData objectAtIndex:indexPath.row] objectForKey:#"status"] isEqualToString:#"active"]) {
//Set the detail text label colour
cell.detailTextLabel.textColor = RGB (35,179,0);
}
return cell;
}
#end
Basically my table doesn't populate. Please could someone help?
- (void)viewDidLoad
{
[super viewDidLoad];
_modelDroplets = [[Droplets alloc]init];
self.tableData = [_modelDroplets dropletsArray];
[_modelDroplets getDropletsList];
[self.tableView reloadData]; // reload the droplets table controller
}
In this method you are fetching droplets from a webservice. It is asynchronous, by the time tableView reloads the data it might not have completed fetching the data. You need to have a callback which will reload the tableView on completion of webservice.
EDIT :
Create a class method in Droplets to fetch all data
//Droplets.h
typedef void (^NSArrayBlock)(NSArray * array);
typedef void (^NSErrorBlock)(NSError * error);
//Droplets.m
+ (void)getDropletsWithCompletion:(NSArrayBlock)arrayBlock onError:(NSErrorBlock)errorBlock
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:kDigialOceanApiURL];
[urlRequest setHTTPMethod:#"GET"];
[urlRequest setCachePolicy:NSURLCacheStorageNotAllowed];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) {
if (error) {
errorBlock(error);
}else{
NSError *serializationError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:&serializationError];
arrayBlock(json[#"droplets"]);
}
}];
}
//DropletsList.h
- (void)viewDidLoad
{
[super viewDidLoad];
[Droplets getDropletsWithCompletion:^(NSArray *array) {
self.modelDroplets = droplets;
[self.tableView reloadData];
} onError:^(NSError *error) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}];
}
Disclaimer : Tested and verified :)

Resources