Ey guys, here is my code:
#import <Foundation/Foundation.h>
#interface Updater : NSObject {
NSURLConnection *updateConnection;
NSURLDownload *downloadConnection; //error at this line
}
#end
This is the error I am getting at the marked line:
Updater.h:15: error: expected specifier-qualifier-list before 'NSURLDownload'
Any idea why I am getting this error message? I have included the Foundation Framework and am stumped as to why the compiler is complaining, especially considering it doesn't complain at all about NSURLConnection. Thanks!
NSURLDownload class is available on MacOS only, on iOS you should use NSURLConnection. From docs:
iOS Note: The NSURLDownload class is
not available in iOS, because
downloading directly to the file
system is discouraged. Use the
NSURLConnection class instead
If you have large chunk of data to download and want to avoid memory issues you can use NSFileHandle class in connection delegate to write received data directly to disk instead of keeping it in memory.
As you read in iOS Note: it's not possible to use NSURLDownload on ios yet, but the link provided below shows you a simple way to download whatever you want from the net to the application sand box. Hope you like it.
Cheers
http://www.ifans.com/forums/showthread.php?t=169611
------------ Below is the link ------------
//
// UIDownloadBar.h
// UIDownloadBar
//
// Created by John on 3/20/09.
// Copyright 2009 Gojohnnyboi. All rights reserved.
//
#import <UIKit/UIKit.h>
#class UIProgressView;
#protocol UIDownloadBarDelegate;
#interface UIDownloadBar : UIProgressView {
NSURLRequest* DownloadRequest;
NSURLConnection* DownloadConnection;
NSMutableData* receivedData;
NSString* localFilename;
id<UIDownloadBarDelegate> delegate;
long long bytesReceived;
long long expectedBytes;
float percentComplete;
}
- (UIDownloadBar *)initWithURL:(NSURL *)fileURL progressBarFrame:(CGRect)frame timeout:(NSInteger)timeout delegate:(id<UIDownloadBarDelegate>)theDelegate;
#property (nonatomic, readonly) NSMutableData* receivedData;
#property (nonatomic, readonly, retain) NSURLRequest* DownloadRequest;
#property (nonatomic, readonly, retain) NSURLConnection* DownloadConnection;
#property (nonatomic, assign) id<UIDownloadBarDelegate> delegate;
#property (nonatomic, readonly) float percentComplete;
#end
#protocol UIDownloadBarDelegate<NSObject>
#optional
- (void)downloadBar:(UIDownloadBar *)downloadBar didFinishWithData:(NSData *)fileData suggestedFilename:(NSString *)filename;
- (void)downloadBar:(UIDownloadBar *)downloadBar didFailWithError:(NSError *)error;
- (void)downloadBarUpdated:(UIDownloadBar *)downloadBar;
#end
//
// UIDownloadBar.m
// UIDownloadBar
//
// Created by John on 3/20/09.
// Copyright 2009 Gojohnnyboi. All rights reserved.
//
#import "UIDownloadBar.h"
#implementation UIDownloadBar
#synthesize DownloadRequest,
DownloadConnection,
receivedData,
delegate,
percentComplete;
- (UIDownloadBar *)initWithURL:(NSURL *)fileURL progressBarFrame:(CGRect)frame timeout:(NSInteger)timeout delegate:(id<UIDownloadBarDelegate>)theDelegate {
self = [super initWithFrame:frame];
if(self) {
self.delegate = theDelegate;
bytesReceived = percentComplete = 0;
localFilename = [[[fileURL absoluteString] lastPathComponent] copy];
receivedData = [[NSMutableData alloc] initWithLength:0];
self.progress = 0.0;
self.backgroundColor = [UIColor clearColor];
DownloadRequest = [[NSURLRequest alloc] initWithURL:fileURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:timeout];
DownloadConnection = [[NSURLConnection alloc] initWithRequest:DownloadRequest delegate:self startImmediately:YES];
if(DownloadConnection == nil) {
[self.delegate downloadBar:self didFailWithError:[NSError errorWithDomain:#"UIDownloadBar Error" code:1 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:#"NSURLConnection Failed", NSLocalizedDescriptionKey, nil]]];
}
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
NSInteger receivedLen = [data length];
bytesReceived = (bytesReceived + receivedLen);
if(expectedBytes != NSURLResponseUnknownLength) {
self.progress = ((bytesReceived/(float)expectedBytes)*100)/100;
percentComplete = self.progress*100;
}
[delegate downloadBarUpdated:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.delegate downloadBar:self didFailWithError:error];
[connection release];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
expectedBytes = [response expectedContentLength];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.delegate downloadBar:self didFinishWithData:self.receivedData suggestedFilename:localFilename];
[connection release];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
}
- (void)dealloc {
[localFilename release];
[receivedData release];
[DownloadRequest release];
[DownloadConnection release];
[super dealloc];
}
#end
Related
I have created a class that will collect data from url data asynchronously, however my understanding of callbacks or whatever is not clear and I'm trying to find a simple way to reuse my class by having the calling method wait for data to be returned or set within the ApiManager class. I just need something to wakeup in another class when that process has been completed. Some processes have single request and others have multiple, why you will notice that I'm using [connection description] within the ApiManager class.
ApiManager.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface ApiManager : NSObject<NSURLConnectionDelegate>
{
NSMutableDictionary *_dataDictionary;
}
- (void)urlRequest:(NSURLRequest *)url;
#property (strong, nonatomic) NSMutableArray *results;
#end
ApiManager.m
#import "ApiManager.h"
#implementation ApiManager
- (void)urlRequest:(NSURLRequest *)url {
[[NSURLConnection alloc] initWithRequest:url delegate:self];
}
// basic connection classes
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSMutableData *responceOjb = _dataDictionary[ [connection description] ];
[_dataDictionary setObject:responceOjb forKey:[connection description]];
}
// append any data we find
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableData *responceOjb = _dataDictionary[ [connection description] ];
[responceOjb appendData: data];
[_dataDictionary setObject:responceOjb forKey:[connection description]];
}
// --
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
// wrap up and close the connect, move objects over to results or something
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_results addObject:[connection description]];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
NSLog(#"%#",error);
}
#end
The main View Controller test:
#import "ViewController.h"
#import "ApiManager.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self DoThisTest];
}
-(void)DoThisTest {
ApiManager *api = [[ApiManager alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",#"http://google.com"]]];
[api urlRequest:request];
if([api results]) {
NSLog(#"GOT DATA");
}
}
Well, there are a few options. You could add a block property onto your ApiManager class:
#property (copy, nonatomic) void (^doneHandler)();
And then invoke that block like so:
self.doneHandler();
You would invoke the block when you deem it appropriate (say, in your connectionDidFinishLoading: method).
With this approach, the definition of the block (callback) would happen in your view controller and look something like:
ApiManager *apiManager = [[ApiManager alloc] init];
apiManager.doneHandler = ^{
// Do whatever you need to do here.
};
Alternatively, you could add a method to your ApiManager with a signature like this:
- (void)sendRequestWithURL:(NSURL*)url completion:(void(^)())completion;
And use NSURLConnection's (or, better, NSURLSession's) block-based APIs. Those APIs have callbacks built in and you would simply invoke completion(); inside of the completion block of -[NSURLSession sendAsynchronousRequest:completion:].
Finally, you could define an ApiManagerDelegate protocol.
- (void)apiManagerDidFinishReceivingData:(ApiManager*)sender;
And add a delegate property to your ApiManager class.
#property (weak, nonatomic) id<ApiManagerDelegate>delegate;
Assign the delegate of your ApiManager in your ViewController:
ApiManager *apiManager = [[ApiManager alloc] init];
apiManager.delegate = self;
Call the delegate method inside of your implementation of NSURLConnectionDelegate's callbacks in ApiManager like so:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_results addObject:[connection description]];
[self.delegate apiManagerDidFinishReceivingData:self];
}
And implement the delegate method in ViewController:
- (void)apiManagerDidFinishReceivingData:(ApiManager*)sender {
// Do what you want to.
}
As an addendum, there are networking libraries available that do a lot of the heavy lifting and busy-work for you, most notably AFNetworking, if you're just trying to get stuff done. And, even if this is more of an academic exercise where you're trying to understand the patterns, looking at AFNetworking's APIs and implementation (it's open source) would be highly instructive.
Cheers
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
}
}
I'm having a very puzzling issue with NSURLConnection. It is returning the data in another thread or something, things are not working in the proper order. I'm supposed to receive the json data then parse it, but the data is actually being received after the parser fails. Here is the log.
2013-09-22 14:44:40.454 WebServiceExample[39306:a0b] Parsing Error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0xa0b47e0 {NSDebugDescription=No value.}
2013-09-22 14:44:41.312 WebServiceExample[39306:a0b] Succeeded! Received 112 bytes of data
I'm making a small framework to ease my life to connect to rails API, I'm calling it objective rails OR, but I already spent the entire weekend trying to figure out what I'm doing wrong. Here's the code:
#interface ORObject : NSObject
#property (nonatomic, retain) NSMutableDictionary *properties;
#property (nonatomic, retain) ORWebManager *webManager;
#property (nonatomic, retain) NSString *route;
#property (nonatomic, retain) NSString *singularClassName;
- (id) initWithRoute:(NSString *) route webManager:(ORWebManager *) webManager;
- (id) initWithRoute:(NSString *) route;
- (ORObject *) find:(NSInteger) identifier;
#end
Here is the implementation of find:
- (ORObject *) find:(NSInteger) identifier
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#/%ld.%#", [ ORConfig sharedInstance].baseURL, self.route, (long)identifier, self.webManager. parser.contentType]];
ORObject *object = [self.webManager httpGet:url];
if (object) {
object.route = self.route;
object.webManager = self.webManager;
}
return object;
}
Here is the httpGet method of the ORWebManager class
- (ORObject *) httpGet:(NSURL *)url
{
ORGetRequester *requester = [[ORGetRequester alloc] init];
NSMutableData *data = [requester request:url];
return [self.parser toObject:data];
}
The superclass of ORGetRequester is ORHTTPRequester and it implements the NSURLConnectionDelegate protocol methods
#interface ORHTTPRequester : NSObject<NSURLConnectionDelegate>
#property (nonatomic, retain) NSMutableData *receivedData;
#property (nonatomic, retain) NSMutableURLRequest *req;
#property (nonatomic, retain) NSURLConnection *connection;
#end
#implementation ORHTTPRequester
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[self.receivedData length]);
}
#end
And finally here is ORGetRequester, this is where the "magic" happens.
#interface ORGetRequester : ORHTTPRequester
- (NSMutableData *) request:(NSURL *)url;
#end
#implementation ORGetRequester
- (NSMutableData *) request:(NSURL *)url
{
self.req = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePol icy
timeoutInterval:60.0];
self.receivedData = [[NSMutableData alloc] init];
self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];
if (!self.connection) {
NSLog(#"Connection Failed");
}
return self.receivedData;
}
#end
self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];
returns immediately because it starts an async operation. You need to parse the data when connectionDidFinishLoading: is called.
Or consider using:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
The completion block will be called when the http operations is complete. Process the data in the completion block and notify any object that needs the data. This is probably a good bet if you do not need the other delegate methods.
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 :)
This question already has answers here:
How do I create delegates in Objective-C?
(20 answers)
Closed 9 years ago.
On iOS, how do I create a delegate (user defined)?
First define a declare a delegate like this -
#protocol IconDownloaderDelegate;
Then create a delegate object like this -
#interface IconDownloader : NSObject
{
NSIndexPath *indexPathInTableView;
id <IconDownloaderDelegate> delegate;
NSMutableData *activeDownload;
NSURLConnection *imageConnection;
}
Declare a property for it -
#property (nonatomic, assign) id <IconDownloaderDelegate> delegate;
Define it -
#protocol IconDownloaderDelegate
- (void)appImageDidLoad:(NSIndexPath *)indexPath;
#end
Then you can call methods on this delegate -
[delegate appImageDidLoad:self.indexPathInTableView];
Here is the complete source code of the image downloader class -
.h file -
#class AppRecord;
#class RootViewController;
#protocol IconDownloaderDelegate;
#interface IconDownloader : NSObject
{
AppRecord *appRecord;
NSIndexPath *indexPathInTableView;
id <IconDownloaderDelegate> delegate;
NSMutableData *activeDownload;
NSURLConnection *imageConnection;
}
#property (nonatomic, retain) AppRecord *appRecord;
#property (nonatomic, retain) NSIndexPath *indexPathInTableView;
#property (nonatomic, assign) id <IconDownloaderDelegate> delegate;
#property (nonatomic, retain) NSMutableData *activeDownload;
#property (nonatomic, retain) NSURLConnection *imageConnection;
- (void)startDownload;
- (void)cancelDownload;
#end
#protocol IconDownloaderDelegate
- (void)appImageDidLoad:(NSIndexPath *)indexPath;
#end
.m file -
#import "IconDownloader.h"
#import "MixtapeInfo.h"
#define kAppIconHeight 48
#define TMP NSTemporaryDirectory()
#implementation IconDownloader
#synthesize appRecord;
#synthesize indexPathInTableView;
#synthesize delegate;
#synthesize activeDownload;
#synthesize imageConnection;
#pragma mark
- (void)dealloc
{
[appRecord release];
[indexPathInTableView release];
[activeDownload release];
[imageConnection cancel];
[imageConnection release];
[super dealloc];
}
- (void)startDownload
{
self.activeDownload = [NSMutableData data];
// alloc+init and start an NSURLConnection; release on completion/failure
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:appRecord.mixtape_image]] delegate:self];
self.imageConnection = conn;
[conn release];
}
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
}
#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.activeDownload = nil;
// Release the connection now that it's finished
self.imageConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
self.appRecord.mixtape_image_obj = image;
self.activeDownload = nil;
[image release];
// Release the connection now that it's finished
self.imageConnection = nil;
// call our delegate and tell it that our icon is ready for display
[delegate appImageDidLoad:self.indexPathInTableView];
}
#end
and here is how we use it -
#import "IconDownloader.h"
#interface RootViewController : UITableViewController <UIScrollViewDelegate, IconDownloaderDelegate>
{
NSArray *entries; // the main data model for our UITableView
NSMutableDictionary *imageDownloadsInProgress; // the set of IconDownloader objects for each app
}
in .m file -
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
iconDownloader.indexPathInTableView = indexPath;
iconDownloader.delegate = self;
[imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
[iconDownloader release];
}
}
here is delegate gets called automatically -
// called by our ImageDownloader when an icon is ready to be displayed
- (void)appImageDidLoad:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader != nil)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:iconDownloader.indexPathInTableView];
// Display the newly loaded image
cell.imageView.image = iconDownloader.appRecord.appIcon;
}
}
This is basic concepts to create a own delegate
Delegates are very useful to control transfer within the array of view controllers in app manually. Using delegates you can manage the control flow very well.
here is small example of own delegates....
Create a protocol class.... (.h only)
SampleDelegate.h
#import
#protocol SampleDelegate
#optional
#pragma Home Delegate
-(NSString *)getViewName;
#end
Import above protocol class in the class whom you want to make delegate of another class. Here in my ex. I m using AppDelegate to make delegate of The HomeViewController's Object.
also add above DelegateName in Delegate Reference < >
ownDelegateAppDelegate.h
#import "SampleDelegate.h"
#interface ownDelegateAppDelegate : NSObject <UIApplicationDelegate, SampleDelegate> {
}
ownDelegateAppDelegate.m
//setDelegate of the HomeViewController's object as
[homeViewControllerObject setDelegate:self];
//add this delegate method definition
-(NSString *)getViewName
{
return #"Delegate Called";
}
HomeViewController.h
#import
#import "SampleDelegate.h"
#interface HomeViewController : UIViewController {
id<SampleDelegate>delegate;
}
#property(readwrite , assign) id<SampleDelegate>delegate;
#end
HomeViewController.h
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UILabel *lblTitle = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
lblTitle.text = [delegate getViewName];
lblTitle.textAlignment = UITextAlignmentCenter;
[self.view addSubview:lblTitle];
}