I am developing a messaging client (as Whatsapp or Line).
I am in troubles because I need to support multiple-downloading files (and uploading), showing a progress bar into each message that have a current downloading process.
The multi-download and upload process is already developed (and works fine), the issue comes when I try to show that progress into the message progress bar.
My "solution" (I don't know if there is a better way to do it) is adding a field "uploaded" and other "downloaded" into the Message entity in CoreData, which save the upload percent or the download percent, and update that field each time that NSURLConnection calls (in upload progress, in download is different):
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
but if I try to update the CoreData context inside that method, my user interface freeze.
Ok, that is my problem, I am going to show you the code:
This is my Core Data Message entity:
#property (nonatomic, retain) NSString *ident;
#property (nonatomic, retain) NSString *localId;
#property (nonatomic, retain) NSString *body;
#property (nonatomic, retain) NSString *contentSize;
#property (nonatomic, retain) NSNumber *uploaded;
#property (nonatomic, retain) NSNumber *downloaded;
#property (nonatomic, retain) Avatar *avatar;
#property (nonatomic, retain) Thumbnail *thumbnail;
Here is my class to manage every HTTP connection and here I try to update that entity.
#implementation HTTPConnection
//SOME CODE...
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
if (self.localId) {
float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
[self performSelectorOnMainThread:#selector(saveProgressInDatabase:) withObject:[NSNumber numberWithFloat:progress] waitUntilDone:YES];
}
}
- (void)saveProgressInDatabase:(NSNumber *)progress
{
NSManagedObjectContext *context = [[[AppDelegate alloc]init]managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:MESSAGE_ENTITY
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"localId = %#", self.localId];
[fetchRequest setPredicate:predicate];
NSError *coreError = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&coreError];
Message *currentMessage = [fetchedObjects objectAtIndex:0];
[currentMessage setUploaded:progress];
[context save:&coreError];
[[NSNotificationCenter defaultCenter] postNotificationName:UPLOAD_UPDATED_NOTIFICATION object:self];
}
And here I show the message list and send the messages in a table view:
#implementation TimelineViewController
//SOME CODE...
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateTable)
name:UPLOAD_UPDATED_NOTIFICATION
object:nil];
}
- (void)updateTable
{
NSError *error;
[[self fetchedResultsController] performFetch:&error];
if ([[_fetchedResultsController sections]count]>0) {
[table reloadData];
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([table numberOfRowsInSection:[[_fetchedResultsController sections]count]-1] - 1) inSection:[[_fetchedResultsController sections]count]-1];
[table scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
}
}
//SOME CODE...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Message *info = [_fetchedResultsController objectAtIndexPath:indexPath];
//MORE CODE...
if ([info contentSize]!=NULL) {
[cell.progressBar setHidden:NO];
[cell.progressBar setProgress:[[info uploaded]floatValue]];
}
}
//CODE...
- (IBAction)sendMessageAction:(id)sender
{
CommandServices *services = [[CommandServices alloc]init];
[services sendMessageToGroup:message withMedia:attachedMedia withLocalId:localId];
}
I don't know if that is enough to understand my issue, so I am going to ask two main questions:
Is that a nice way to manage multiple-download with multiple progress bar? I mean, saving the progress into the entity... Or, how could I do it?
Why my app is freeze when I upload or download a file? Maybe I am doing too many access to CoreData?
Thanks so much!
My guess is that you should use the following code to get the NSManagedObjectContext:
AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [app managedObjectContext];
instead of:
NSManagedObjectContext *context = [[[AppDelegate alloc]init]managedObjectContext];
Well...after few days thinking about my issue, I realize I was doing too many database access and because of that my app freeze.
I solved it passing to my HTTPConnection object, an instance of the progress bar I want to manage. And I save the progress download/upload only when finishing.
So that is my solution:
In HTTPConnection:
+ (void)setProgressBar:(UIProgressView *)progress
{
[downloadConnection setProgressBar:progress];
}
//CODE...
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
if (self.progressBar) {
self.progressBar.progress = progress;
}
if (progress == 1.0) {
[self saveProgressInDatabase:[NSNumber numberWithFloat:progress]];
}
}
And in my message list:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Message *info = [_fetchedResultsController objectAtIndexPath:indexPath];
//MORE CODE...
if (([info contentSize]!=NULL)&&([[info uploaded]floatValue]<1.0)) {
[HTTPConnection setProgressBar:cell.progressBar];
[cell.progressBar setHidden:NO];
} }
Thanks for your help!
Related
I made an application for storing data in CD. I want to just simply write the things into the console but I can't get printed on the console. Am I doing something wrong?
Here is my code whole demo application.
Here is my screen shot of Core_Data_Demo_xcdatamodeld
// AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
// AppDelegate.h
-(BOOL)createNewRectangleWithHeight:(NSInteger)heightParam width:(NSInteger)widthParam{
if (heightParam ==0 || widthParam ==0) {
NSLog(#"The height and width must no be 0");
return NO;
}
Rectangle *rect = [NSEntityDescription insertNewObjectForEntityForName:#"Rectangle" inManagedObjectContext:self.managedObjectContext];
if (rect == nil) {
NSLog(#"Failed to create new Rectangle");
return NO;
}
rect.height = [NSNumber numberWithInt:heightParam];
rect.width = [NSNumber numberWithInt:widthParam];
NSError *savingError = nil;
if ([self.managedObjectContext save:&savingError]) {
return YES;
} else {
NSLog(#"Failed to save new person. Error = %# ",savingError);
}
return YES;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self createNewRectangleWithHeight:2 width:2];
return YES;
}
You are not able to see any statements because (I suppose) things run correctly.
If you want to retrieve data and print in the console you need to run a different method like printData or whatever you want. This method should set up a NSFetchRequest and execute it against your entity Rectangle.
- (void)printData {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Rectangle"];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if(error) {
// An error occurred
} else {
// See the results
}
}
Usage
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// [self createNewRectangleWithHeight:2 width:2];
[self printData];
return YES;
}
You should comment the createNew... method otherwise you will see multiple entries (equal to the number of times you've run the application) of Rectangle objects with the same width and height.
The code which you have shown is for adding values using core data, If you do not get any error in the NSError object while you are adding the values then data is successfully added inside the sqlite file.
To check the added values what you can do is use the SqliteManager addon from firefox and open the sqlite file (You can get the sqlite file location using NSHomeDirectory() method for it and then jump to the documents folder).
If you don't want the addon way you can always use NSFetchRequest to pull your data given below is the code for the same
- (NSManagedObjectContext*)getManagedObjectContext{
AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
return appDelegate.managedObjectContext;
}
- (void)fetchdataFromDatabase{
NSManagedObjectContext *appContext = [self getManagedObjectContext];
if(appContext!=nil){
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YOUR_ENTITY_NAME" inManagedObjectContext:appContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [appContext executeFetchRequest:fetchRequest
error:&error];
if(error!=nil && fetchedObjects.count!=0){
// print your data here
}else{
//Print the error here
}
}
}
I can successfully send and receive messages with PubNub, the problem comes when i try to display content from a message and load it a UITableViewCell's UITextView.
The Second TEST LOG writes out the whole message, that i send from my iPhone (i've already tried it with the Dev Console), but after this the app crashes.
[__NSCFDictionary length]: unrecognized selector sent to instance
I know there is something wrong with a dictionary, but i can't figure it out. I'm using only one NSDictionary for the message i send via PubNub and it "arrives" to the console, therefore I think it works properly. As you can see in the code i've tried some variations, but without any success.
UPDATE
It's working if i send NSString instead of NSDictionary.
#interface ViewController ()
#property (nonatomic, strong) NSString *myIncomeMessage;
#property (nonatomic, strong) NSString *messageFromDict;
#property (nonatomic, strong) NSArray *twoChannels;
#property (nonatomic, strong) NSDictionary *messagePbnb;
//#property (nonatomic, strong) PNMessage *messageNew;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
PNChannel *channel_2 = [PNChannel channelWithName:current.username shouldObservePresence:NO];
PNChannel *channel_1 = [PNChannel channelWithName:self.messageRecipient shouldObservePresence:NO];
[PubNub subscribeOnChannels:self.twoChannels];
[PubNub requestHistoryForChannel:channel_1 from:nil to:nil limit:100 reverseHistory:YES];
[PubNub requestHistoryForChannel:channel_2 from:nil to:nil limit:100 reverseHistory:YES];
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:self withBlock:^(PNMessage *message) {
NSLog(#"OBSERVER: Channel: %#, Message: %#", message.channel.name, message.message);
NSLog(#"Sample TEST LOG %#", message.message);
self.myIncomeMessage = message.message;
NSLog(#"Second TEST LOG %#", self.myIncomeMessage);
// self.messageFromDict = [NSString stringWithFormat:keyMessage, self.messagePbnb];
// self.messageFromDict = [NSString stringWithFormat:keyMessage, message];
}];
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(reloadTable) userInfo:nil repeats:YES];
[self setupUIForInput];
}
- (IBAction) inputContent {
NSString *messageContent = self.textView.text;
PNChannel *channel_1 = [PNChannel channelWithName:self.messageRecipient shouldObservePresence:NO];
PNChannel *channel_2 = [PNChannel channelWithName:senderUser.username shouldObservePresence:NO];
self.twoChannels = #[channel_1,channel_2];
[PubNub subscribeOnChannels: self.twoChannels];
self.messagePbnb = #{ #"keyMessage": messageContent, #"keySenderUser": self.senderUser.username, #"keyRecieverChannel": self.messageRecipient} ;
[PubNub sendMessage: self.messagePbnb toChannel:channel_1];
[PubNub sendMessage: self.messagePbnb toChannel:channel_2];
[self.textView resignFirstResponder];
[self reloadInputViews];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
OutputTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellThree"];
cell.textOutput.text = self.myIncomeMessage;
-(void)reloadTable{
[tableViewThree reloadData];
}
#sabin, here is an example, using an observer -- you could do a similar thing via a delegate:
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:self
withBlock:^(PNMessage *message) {
id messageData = message.message;
if ([messageData isKindOfClass:[NSDictionary class]]) {
NSString *messageString = [NSString stringWithFormat:#"foo: %#, and bar: <%#>",
[(NSDictionary *)messageData valueForKey:#"foo"],
[(NSDictionary *)messageData valueForKey:#"bar"]];
}
}];
If you then published this from the Web Console (pubnub.com/console):
{"foo":"hey", "bar":"you!"}
You would be able to render a string from the above code (via messageString) that looked like this:
foo: hey, and bar: you!
Let me know if that helped!
I have a UIView that contains a UITableView. These are managed by a UIViewController that inherits the delegate methods UITableViewDelegate and NSFetchedResultsControllerDelegate. I am able to populate the table just fine using the NSFetchedResultsController, however the delegate methods (specifically controllerWillChangeContent) are not called when changes are made to managed objects in the fetchObjects.
I have checked independently that changes are made to the objects, and those changes I have reflected in the cells, however you must manually reload the cells (scrolling up) to see the changes occur. The behavior I am looking for is that when the changes are made to the object (via save), that the NSFetchedResultsControllerDelegate methods fire so I can make the updates to the table view.
Just to be clear, the data is populated from the database when the view loads, so I know the code pulls that just fine.
Header interface
#interface HSAwardsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate>
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#end
init method - Setting up managedObjectContext (uses Magical Record)
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
HSInternalDB *dbInstance = [HSInternalDB getInternalDB];
self.persistentStoreCoordinator = [dbInstance getPersistentStoreCoordinator];
self.managedObjectContext = [NSManagedObjectContext MR_contextWithStoreCoordinator:self.persistentStoreCoordinator];
self.tableView.delegate = self;
self.tableView.dataSource = self;
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
return self;
}
Setting up the NSFetchedResultsController and assigning the delegate
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Award"];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"gameTypeIdentifier" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError *error = nil;
[theFetchedResultsController performFetch:&error];
theFetchedResultsController.delegate = self;
self.fetchedResultsController = theFetchedResultsController;
// Listen for this notification so that the managedObjectContext runs on the main thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
return _fetchedResultsController;
}
- (void)contextChanged:(NSNotification*)notification
{
NSManagedObjectContext *context = [notification object];
if ([context isEqual:self.managedObjectContext]) {
return;
}
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
As far as I can see, all the connections are are made in code or in the storyboard. I know a change is recorded in my database because I am listening for the NSManagedObjectContextDidSaveNotification.
This has me stumped, so I appreciate any suggestions or bread crumbs. I'm happy to answer any questions or post more code if needed!
contextChanged: should be called on MainThread since it involve UI update.
I have been searching for the answer for a couple of weaks now but I just can't seem to find it. I have been trying to get a UISegmentedControl's data to save in the Core Data but I can't do it, it keeps showing me an errors and warnings, hope you can help me.
I have something like this:
#import "DetailScoutingViewController.h"
#interface DetailScoutingViewController ()
#property (strong, nonatomic) IBOutlet UITextField *name;
#property (strong, nonatomic) IBOutlet UITextField *number;
#property (strong, nonatomic) IBOutlet UISegmentedControl *considered;
- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
#property (strong, nonatomic) NSManagedObject *teamData;
#end
#implementation DetailScoutingViewController
#synthesize teamData;
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.teamData) {
[self.name setText:[self.teamData valueForKey:#"name"]];
[self.number setText:[self.teamData valueForKey:#"number"]];
[self.considered setSelectedSegmentIndex:[self.teamData valueForKey:#"considered"]];
}
}
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
if (self.teamData) {
// Update existing device
[self.teamData setValue:self.name.text forKey:#"name"];
[self.teamData setValue:self.number.text forKey:#"number"];
[self.teamData setValue:self.considered.selectedSegmentIndex forKey:#"considered"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Teams" inManagedObjectContext:context];
[newDevice setValue:self.name.text forKey:#"name"];
[newDevice setValue:self.number.text forKey:#"number"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self.navigationController popViewControllerAnimated:YES];
}
- (IBAction)cancel:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
The UITextField's data saves without a problem, the only problem I have is the UISegmentedControl. What should I do?
[self.teamData valueForKey:#"considered"]
This returns what is likely to be an NSNumber instance, but setSelectedSegmentIndex: expects an NSInteger so you should be using:
[self.considered setSelectedSegmentIndex:[[self.teamData valueForKey:#"considered"] integerValue]];
You also need to change the corresponding save code to:
[self.teamData setValue:[NSNumber numberWithInteger:self.considered.selectedSegmentIndex] forKey:#"considered"];
This bit of code is not helping:
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
change to:
if ([delegate respondsToSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
At some point in some other controller you should be setting ...teamData = .... If you aren't, then your controller will always be creating a new Teams managed object and inserting it into the data store. In this case, you don't set the self.considered.selectedSegmentIndex so you will never store it. It's only ever stored when you already have a Teams object.
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 :)