Decoder crashing through a bluetooth connection - ios

I have an object that I want to send to another device via bluetooth. I have successfully setup the bluetooth connection and transferred an encoded NSString; however, I haven't figured out how to use the archiving and encoding tools correctly to send an Object.
I want to send the object defined below called ChatMessage. It implements the NSCoding delegate methods initWithCoder and encodeWithCoder as seen below.
In the second code snippet, I have the code for sending and receiving the data i.e. the methods that result in the de-encoder being called.
It keeps crashing on the last line of the decode method. I've been struggling to figure it out what is going wrong. Any help would be greatly appreciated!
#interface ChatMessage : NSObject <NSCoding> {
NSString *sender;
NSString *message;
}
#property (nonatomic, retain) NSString *sender;
#property (nonatomic, retain) NSString *message;
#end
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:sender forKey:#"sender"];
[coder encodeObject:message forKey:#"message"];
}
- (id)initWithCoder:(NSCoder *)coder {
sender = [[coder decodeObjectForKey:#"sender"] retain];
message = [[coder decodeObjectForKey:#"message"] retain];
return self;
}
In my View, the protocol for the PeerPicker Delegate functions.
- (void) receiveData:(NSData *)data
fromPeer:(NSString *)peer
inSession:(GKSession *)session
context:(void *)context {
ChatMessage *aMsg = [[ChatMessage alloc] init];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
#try {
aMsg = [unarchiver decodeObjectForKey:#"myMessage"];
}
#catch (NSException *exception) {
NSLog(#"Error: %#", exception);
}
#finally {
}
if (!messages) messages = [[NSMutableArray alloc] init];
[messages addObject:aMsg];
// reload the table
[messageList reloadData];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}

--- The code was crashing because I had
[data release];
I found this using the instruments tool.

Related

Background fetch and refresh completed after viewDidLoad in iOS 10

I'm trying to implement background fetch as well as refresh in iOS 10.
I'm using XML parsing to parse the data and then storing it in a file in the document's directory. For parsing XML I'm using a custom class (XMLParser) that confirms the NSXMLParserDelegate protocol.
The background fetch works fine. But I'm having problems in displaying the refreshed data, both when I click on the refresh button as well as in viewDidLoad.
I'm calling the refreshData method in viewDidLoad.
Here's how far I've gotten.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//--Set background fetch--//
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}
...
#pragma mark Background data fetch methods
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDate *fetchStart = [NSDate date];
ArtsViewController *artsViewController = (ArtsViewController *)self.window.rootViewController;
[artsViewController fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
NSLog(#"Background Fetch Duration: %f seconds", timeElapsed);
}];
}
ArtsViewController.h
#interface ArtsViewController : UIViewController <UIPageViewControllerDataSource>
#property BOOL newsAvailable;
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // No problems here
#end
ArtsViewcontroller.m
#interface ArtsViewController ()
#property (nonatomic, strong) NSArray *arrNewsData;
-(void)refreshData;
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray;
#end
...
#implementation ArtsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshData];
//--Load the file that saves news--//
[self loadNews];
if (_newsAvailable == YES)
{
[self setupPageViewController];
}
else
{
[self showNoNewsMessage];
}
}
...
#pragma mark Data Fetch methods
-(void)refreshData{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
[self performNewFetchedDataActionsWithDataArray:dataArray];
}
else{
NSLog(#"%#", [error localizedDescription]);
}
}];
}
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray{
// 1. Initialize the arrNewsData array with the parsed data array.
if (self.arrNewsData != nil) {
self.arrNewsData = nil;
}
self.arrNewsData = [[NSArray alloc] initWithArray:dataArray];
// 2. Write the file and reload the view.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docDirectory = [paths objectAtIndex:0];
NSString * newsFilePath = [NSString stringWithFormat:#"%#",[docDirectory stringByAppendingPathComponent:#"arts2"]]; // NewsFile
if (![self.arrNewsData writeToFile:newsFilePath atomically:YES]) {
_newsAvailable = NO;
NSLog(#"Couldn't save data.");
}
else
{
_newsAvailable = YES;
NSLog(#"Saved data.");
[self viewWillAppear:YES];
}
}
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
NSDictionary *latestDataDict = [dataArray objectAtIndex:0];
NSString *latestTitle = [latestDataDict objectForKey:#"title"];
NSDictionary *existingDataDict = [self.arrNewsData objectAtIndex:0];
NSString *existingTitle = [existingDataDict objectForKey:#"title"];
if ([latestTitle isEqualToString:existingTitle]) {
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"No new data found.");
}
else{
[self performNewFetchedDataActionsWithDataArray:dataArray];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"New data was fetched.");
}
}
else{
completionHandler(UIBackgroundFetchResultFailed);
NSLog(#"Failed to fetch new data.");
}
}];
}
...
#pragma mark IBActions
- (IBAction)reloadNews:(UIBarButtonItem *)sender
{
[self viewDidLoad];
}
I've debugged the application and found that after viewDidLoad
completes execution, the data file is written but the view isn't
updated. I've also tried calling the refreshData method in the main
thread, but there's no change.
after viewDidLoad is complete the showNoNewNews method is called.
I'm suspecting that my logic isn't wrong but implementation is. Threads at play here..
Any help would be appreciated.
Update:
Hope this helps those with similar problems...
I moved the logic of viewDidLoad to a different method, called the method for the first time in viewDidLoad and again in refreshData, after
[self performNewFetchedDataActionsWithDataArray:dataArray];

Send NSArray Via AirDrop

I have an NSMutableArray self.certificates
This array is made up of saved strings and core data. I want to send this through AirDrop. I have checked out serialization and and im trying to send it with the folowing
- (void)send{
NSData *jsonData2 = [NSJSONSerialization dataWithJSONObject:self.certificates options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData2 encoding:NSUTF8StringEncoding];
NSLog(#"Electrical Certificates List:\n%#", jsonString);
UIActivityViewController *activityCtr = [[UIActivityViewController alloc] initWithActivityItems:#[jsonString]
applicationActivities:nil];
NSMutableArray *excludedActivities = [self iOSActivities].mutableCopy;
[excludedActivities addObject:UIActivityTypeAddToReadingList];
[excludedActivities addObject:UIActivityTypePostToFlickr];
[excludedActivities addObject:UIActivityTypePostToTencentWeibo];
[excludedActivities addObject:UIActivityTypePostToVimeo];
[activityCtr setExcludedActivityTypes:excludedActivities];
[self presentViewController:activityCtr
animated:YES
completion:nil];
}
This gives me the following error
'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Certificate)'
I have converted to data to a string so not sure what im missing here
Ive researched NSInvalidArgumentException, reason: 'Invalid type in JSON write (__NSDate)' and How to send NSArray to web service
The exception is thrown by JSONSerialization, before the array is converted to data.
To share a custom data type, you'll want to implement NSCoding and UIActivityItemSource on your model object:
#interface CertificateGroup : NSObject <NSCoding, UIActivityItemSource>
#property(copy, nonatomic) NSArray *certificates;
#end
#implementation CertificateGroup
- (void)encodeWithCoder:(NSCoder *)aCoder {
// Save all your custom properties
[aCoder encodeObject:self.certificates forKey:#"certificates"]l
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
// Read back properties
self.certificates = [aDecoder decodeObjectForKey:#"certificates"];
}
return self;
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
//Let the activity view controller know NSData is being sent by passing this placeholder.
return [NSData data];
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
//Serialize this object for sending. NSCoding protocol must be implemented for the serialization to occur.
return [NSKeyedArchiver archivedDataWithRootObject:self];
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType {
return #"com.mycompany.myapp.certificates";
}
#end
Then, when you create your activity view controller:
CertificatesGroup *group = [CertificatesGroup new];
group.certificates = self.certificates;
UIActivityViewController *activityCtr = [[UIActivityViewController alloc] initWithActivityItems:#[group]
applicationActivities:nil];
...
You're app delegate should implement -application:openURL:sourceApplication:annotation: and decode the incoming certificates.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSData *groupData = [NSData dataWithContentsOfURL:url];
CertificatesGroup *group = [NSKeyedUnarchiver unarchiveObjectWithData:groupData];
NSLog(#"%#", group.certificates);
return YES;
}
For more info, see Apple's AirDropSample project, especially APLProfile.h/.m, APLProfileViewController.h/.m and AppDelegate.m.

iOS WebService doesn't seem to be starting. Anyway I can find out if its doing anything?

I have built a WebService to retrieve user specific reports using a PHP API. I am fairly new to this so I followed some tutorials and help pages to build the web service. It doesn't seem to run at all so I am thinking I stuffed up or just incorrectly put code in that didn't belong as I was following multiple tutorials etc to get the desired result.
here is the code for .m file of the web service:
#import "reportsTestViewController.h"
#import "ReportsDataObject.h"
#interface reportsTestViewController ()
#end
#implementation reportsTestViewController
#synthesize label;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.tesg.com.au/allCustBuild.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
//connect
label.text = #"connecting...";
} else {
//error
}
}
-(void)setupReportsFromJSONArray:(NSData*)dataFromReportsArray{
NSError *error;
NSMutableArray *reportsArray = [[NSMutableArray alloc] init];
NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromReportsArray options:0 error:&error];
if(error){
NSLog(#"error parsing the json data from server with error description - %#", [error localizedDescription]);
}
else {
reportsArray = [[NSMutableArray alloc] init];
for(NSDictionary *eachReport in arrayFromServer)
{
ReportsDataObject *report = [[ReportsDataObject alloc] initWithJSONData:eachReport];
[reportsArray addObject:report];
}
//Now you have your reportsArray filled up with all your data objects
}
}
-(void)connectionWasASuccess:(NSData *)data{
[self setupReportsFromJSONArray:data];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
//We check against table to make sure we are displaying the right number of cells
// for the appropriate table. This is so that things will work even if one day you
//decide that you want to have two tables instead of one.
if(tableView == reportsTable){
return([theReportsArray count]);
}
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell)
{
//set your configuration of your cell
}
//The beauty of this is that you have all your data in one object and grab WHATEVER you like
//This way in the future you can add another field without doing much.
if([theReportsArray count] == 0){
cell.textLabel.text = #"no reports to show";
}
else{
ReportsDataObject *currentReport = [theReportsArray objectAtIndex:indexPath.row];
cell.textLabel.text = [currentReport buildingName];
// in the future you can grab whatever data you need like this
//[currentPlace placeName], or [currentPlace placeDay];
}
return(cell);
}
#end
and the code for the .h:
#import <UIKit/UIKit.h>
#interface reportsTestViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
IBOutlet UITableView *reportsTable;
IBOutlet UILabel *Label;
NSArray *theReportsArray;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#end
I'm sure it's something really ignorant that i've done but after going back through help pages and tuts, i can't find what I did wrong.
It looks like you are establishing your connection ok, but you are never handling any data that gets sent back. Once you create a connection you need to add the delegate methods to handle the data sent back by the connection.
As mentioned in some of the comments you also need to verify that your php page on the server is indeed giving you the information you expect. By logging out the data string as below in the -connectionDidFinishLoading you will be able to see any data sent back from the server for debugging.
//create an NSMutableData property in your interface
#property (nonatomic, strong) NSMutableData *myDataIvar;
//initialize it when you create your connection
if (connection){
self.myDataIvar = [[NSMutableData alloc] init];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[self.myDataIvar setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.myDataIvar appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Connection Failed: %#", error.userInfo);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//this is where you would parse the data received back from the server
NSString *responseString = [[NSString alloc] initWithData:self.myDataIvar encoding:NSUTF8StringEncoding];
NSLog(#"Received Data: %#",responseString);
[self setupReportsFromJSONArray:self.myDataIvar];
}
This way at least you will be able to see what data you are receiving back from the server.
EDIT:
Also your test page does not seem to be putting out any json data. When I navigate to it all I get is "how".
I put up a quick php page that you can use to test your obj-c code. It will echo back an array with 10 test results to fill your tableview if your code is correct.
//Create your request pointing to the test page
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.codeyouniversity.com/json_test.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
If you want to put the php on your own page for testing this is what I used.
<?php
$jsonArray = array('result1','result2','result3','result4','result5','result6','result7','result8','result9','result10');
echo json_encode($jsonArray);
?>
Here's a link to Apples documentation for the URL loading system as well
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i
try this one if you get result, your url is wrong.I think now itself your url is wrong
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://api.openweathermap.org/data/2.5/weather?q=London,uk"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
//connect
label.text = #"connecting...";
} else {
//error
}
}

Multiple downloading progress bar objective c

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!

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