UIActivityIndicatorView not stopping - ios

I am totally new in iOS developing. Today I learnt what is UIActivityIndicatorView.
Now, I am building a project where I have a table view, I have populated the table using JSON parsing. Now I have added an activity indicator which will spin upto that time till when the table gets populated.
I have started the activity indicator but it's not stopping. Can you guyz tell me where am I wrong? Thanks in advance.
This is my code.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self fetchData];
self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
_mySpinner.hidden = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) fetchData {
[_mySpinner startAnimating];
NSString *strURL = [NSString stringWithFormat:#"http://api.kivaws.org/v1/loans/search.json?status=fundraising"];
NSURL *url = [NSURL URLWithString:strURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
_webData = [NSMutableData data];
}
else{
//error
}
}
#pragma mark Url connection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[_webData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
// self.data parse
NSDictionary *dict= [NSJSONSerialization JSONObjectWithData:self.webData options:kNilOptions error:nil];
self.arrDetail = [dict valueForKey:#"loans"];
[self.mySpinner stopAnimating];
self.mySpinner.hidden = YES;
[self.parserTable reloadData];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
}
#pragma mark Table View Delegates
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
NSDictionary *locationDict = [[self.arrDetail objectAtIndex:indexPath.row]valueForKey:#"location"];
UILabel *lbl1 = (UILabel*)[cell.contentView viewWithTag:1];
lbl1.text = [[self.arrDetail objectAtIndex:indexPath.row]valueForKey:#"name"];
UILabel *lbl2 = (UILabel*)[cell.contentView viewWithTag:2];
lbl2.text = [locationDict valueForKey:#"country"];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.arrDetail count];
}
#end

The Solution
remove this line from the code
self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]
The Problem
You haven't shown the property Declaration of mySpinner. However from your code I can tell that it is an IBOutlet since you have created a new instance of UIActivityIndicatorView and not added it as a subview and you can still see a activity indicator on your view(since it appears and never stops animating).
The reason that it does not stop animating is you call [_mySpinner startAnimating]; on your IBOutlet. Then you create a new instance of UIActivityIndicatorView when you say
self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]
now on whatever methods you call on mySpinner will be called on an activity indicator which is not on your view but the one you created because you lost your reference to it the moment you created a new UIActivityIndicatorView.
Also, since you are new. I would suggest you to use self.mySpinner as far as possible and not to use self and _ interchangeably as both are to be used according to need. The reason for is beyond the scope of your question.

To start Activity indicator:
[cell.indicater startAnimating];
To start Activity indicator:
[cell.indicater stopAnimating];
Also set properties like shown in screenshot.

Since you are assigning a new instance of UIActivityIndicatorView to you outlet property the reference to the one set in InterfaceBuilder is lost.
Just remove :self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];

First initialize your spinner and then call your connection method like below.
...
- (void)viewDidLoad {
[super viewDidLoad];
self.mySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
_mySpinner.hidden = NO;
_mySpinner.center = self.view.center;
[self fetchData];
}
...

Related

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
}
}

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 :)

UITableView has Empty Rows

I'm having a problem with RSS feeds in a table view in an iOS app. I originally tested the RSS feed in it's project with the table view as the root view. I'm trying to get the same functionality in a different project but the table view that displays the list of articles from the RSS feed is blank. The code for the table view in the new project is the same. The only different is that I have a different root view controller that has a bunch of buttons. One button is supposed to go to that table view and it does but the rows are empty. I'm thinking this may be an issue with how the root view controller is set up since I know the table view is populated when that code is run by itself. Here are the AppDelegate files where it sets the root view controller.
//
// KFBAppDelegate.h
// KFBNewsroom
//
// Created by KFB on 10/15/12.
// Copyright (c) 2012 com.kfb. All rights reserved.
//
#import <UIKit/UIKit.h>
#class KFBViewController;
#interface KFBAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) KFBViewController *viewController;
#end
//
// KFBAppDelegate.m
// KFBNewsroom
//
// Created by KFB on 10/15/12.
// Copyright (c) 2012 com.kfb. All rights reserved.
//
#import "KFBAppDelegate.h"
#import "KFBViewController.h"
#import "ListViewController.h"
#import "WebViewController.h"
#import "ActionAlertsViewController.h"
#import "MarketUpdatesViewController.h"
#implementation KFBAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[KFBViewController alloc] initWithNibName:#"KFBViewController" bundle:nil];
ListViewController *lvc = [[ListViewController alloc]initWithStyle:UITableViewStylePlain];
WebViewController *wvc = [[WebViewController alloc]init];
[lvc setWebViewController:wvc];
ActionAlertsViewController *avc = [[ActionAlertsViewController alloc]initWithStyle:UITableViewStylePlain];
[avc setWebViewController:wvc];
MarketUpdatesViewController *mvc = [[MarketUpdatesViewController alloc]initWithStyle:UITableViewStylePlain];
[mvc setWebViewController:wvc];
self.window.rootViewController = self.viewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
//
// ListViewController.h
// Nerdfeed
//
// Created by KFB on 10/16/12.
// Copyright (c) 2012 com.kfb. All rights reserved.
//
#import <Foundation/Foundation.h>
// #interface ListViewController : NSObject
// a forward declaration; we'll import the header in the .m
#class RSSChannel;
#class WebViewController;
#interface ListViewController : UITableViewController
<NSXMLParserDelegate>
{
NSURLConnection *connection;
NSMutableData *xmlData;
RSSChannel *channel;
}
#property (nonatomic, strong)WebViewController *webViewController;
- (void)fetchEntries;
#end
//
// ListViewController.m
// Nerdfeed
//
// Created by KFB on 10/16/12.
// Copyright (c) 2012 com.kfb. All rights reserved.
//
#import "ListViewController.h"
#import "RSSChannel.h"
#import "RSSItem.h"
#import "WebViewController.h"
#implementation ListViewController
#synthesize webViewController;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"%# found a %# element", self, elementName);
if ([elementName isEqual:#"channel"])
{
// If the parser saw a channel, create new instance, store in our ivar
channel = [[RSSChannel alloc]init];
// Give the channel object a pointer back to ourselves for later
[channel setParentParserDelegate:self];
// Set the parser's delegate to the channel object
[parser setDelegate:channel];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// return 0;
return [[channel items]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// return nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
RSSItem *item = [[channel items]objectAtIndex:[indexPath row]];
[[cell textLabel]setText:[item title]];
return cell;
}
- (void)fetchEntries
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc]init];
// Construct a URL that will ask the service for what you want -
// note we can concatenate literal strings together on multiple lines in this way - this results in a single NSString instance
NSURL *url = [NSURL URLWithString:#"http://kyfbnewsroom.com/category/public- affairs/feed"];
// Put that URL into an NSURLRequest
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Create a connection that will exchange this request for data from the URL
connection = [[NSURLConnection alloc]initWithRequest:req delegate:self startImmediately:YES];
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
[self fetchEntries];
}
return self;
}
// This method will be called several times as the data arrives
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
// Add the incoming chunk of data to the container we are keeping
// The data always comes in the correct order
[xmlData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
/* We are just checking to make sure we are getting the XML
NSString *xmlCheck = [[NSString alloc]initWithData:xmlData encoding:NSUTF8StringEncoding];
NSLog(#"xmlCheck = %#", xmlCheck);*/
// Create the parser object with the data received from the web service
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
// Give it a delegate - ignore the warning here for now
[parser setDelegate:self];
//Tell it to start parsing - the document will be parsed and the delegate of NSXMLParser will get all of its delegate messages sent to it before this line finishes execution - it is blocking
[parser parse];
// Get rid of the XML data as we no longer need it
xmlData = nil;
// Reload the table.. for now, the table will be empty
[[self tableView]reloadData];
NSLog(#"%#\n %#\n %#\n", channel, [channel title], [channel infoString]);
}
- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
{
// Release the connection object, we're done with it
connection = nil;
// Release the xmlData object, we're done with it
xmlData = nil;
// Grab the description of the error object passed to us
NSString *errorString = [NSString stringWithFormat:#"Fetch failed: %#", [error localizedDescription]];
// Create and show an alert view with this error displayed
UIAlertView *av = [[UIAlertView alloc]initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Push the web view controller onto the navigation stack - this implicitly creates the web view controller's view the first time through
[[self navigationController]pushViewController:webViewController animated:YES];
// Grab the selected item
RSSItem *entry = [[channel items]objectAtIndex:[indexPath row]];
// Construct a URL with the link string of the item
NSURL *url = [NSURL URLWithString:[entry link]];
// Construct a request object with that URL
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Load the request into the web view
[[webViewController webView]loadRequest:req];
// Set the title of the web view controller's navigation item
[[webViewController navigationItem]setTitle:[entry title]];
}
#end
Your ListViewController class must inherit from UITableViewController
Edit: Sorry I didn't see it, just add the delegate & dataSource
#interface ListViewController : UITableViewController
<NSXMLParserDelegate, UITableViewDelegate, UITableViewDataSource>
Did you add:
-(void)viewDidLoad {
[super viewDidLoad];
[tableView setDelegate: self];
[tableView setDataSource: self];
}
On implementation of ListViewController (Like after #synthesize webViewController;
)?
Make sure you set up the UITableView DataSource so that you can actually display rows. You'll probably need to set the delegate too.
EDIT:
You're still not setting the UITableView delegate or datasource. You need to do that so that the UITableView knows where to get data from. I think the best place for you to put that is in your ListViewController's viewDidLoad method
-(void)viewDidLoad {
[super viewDidLoad];
[tableView setDelegate: self];
[tableView setDataSource: self];
}
You'll also need to add the UITableViewDelegate and UITableViewDataSource protocols to ListViewController.
EDIT 2:
Make sure you're adding the UITableViewDataSource and UITableViewDelegate protocols to ListViewController
#interface ListViewController : UITableViewController
<NSXMLParserDelegate, UITableViewDelegate, UITableViewDataSource>
Also, make sure the viewDidLoad method above is in the ListViewController class. All UITableViewControllers have a tableView property that holds the actual UITableView.
Try to remove this line from ListViewController.h
#property (nonatomic, retain) UITableView *tableView;
Then replace your - (void) viewDidLoad in ListViewController.m by this code:
- (void) viewDidLoad
{
[super viewDidLoad];
[self.tableView setDelegate: self];
[self.tableView setDataSource:self];
}

How to make sure I have only 1 asynchronous NSURLConnection at a time?

I'm creating an app which communicates alot with a server. I want to make sure I have only 1 connection at a time - only 1 request pending. I want so that if I try to send another request, It will wait until the current one is finished before sending the next.
How can I implement This?
Tnx!
I don't know of any automatic mechanism to do this. So you have to write a new class yourself (let's call it ConnectionQueue):
Basically, instead of a creating the NSURLConnection directly, you call a method of your ConnectionQueue class (which should have exactly one instance) taking the NSURLRequest and the delegate as a parameter. Both are added to a queue, i.e. a separate NSArray for the requests and the delegates.
If the arrays contains just one element, then no request is outstanding and you can create a NSURLConnection with the specified request. However, instead of the delegate passed to the method, you pass your ConnectionQueue instance as the delegate. As a result, the connection queue will be informed about all actions of the connection. In most cases, you just forward the callback to the original delegate (you'll find it in the first element of the delegate array).
However, if the outstanding connection completes (connection:didFailWithError: or connectionDidFinishLoading: is called), you first call the original delegate and then remove the connection from the two arrays. Finally, if the arrays aren't empty, you start the next connection.
Update:
Here's some code. It compiles but hasn't been tested otherwise. Furthermore, the implementation of the NSURLConnectionDelegate protocol is incomplete. If you're expecting more than implemented callbacks, you'll have to add them.
Header File:
#import <Foundation/Foundation.h>
#interface ConnectionQueue : NSObject {
NSMutableArray *requestQueue;
NSMutableArray *delegateQueue;
NSURLConnection *currentConnection;
}
// Singleton instance
+ (ConnectionQueue *)sharedInstance;
// Cleanup and release queue
+ (void)releaseShared;
// Queue a new connection
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate;
#end
Implementation:
#import "ConnectionQueue.h"
#implementation ConnectionQueue
static ConnectionQueue *sharedInstance = nil;
+ (ConnectionQueue*)sharedInstance
{
if (sharedInstance == nil)
sharedInstance = [[ConnectionQueue alloc] init];
return sharedInstance;
}
+ (void)releaseShared
{
[sharedInstance release];
sharedInstance = nil;
}
- (id)init
{
if ((self = [super init])) {
requestQueue = [NSMutableArray arrayWithCapacity:8];
delegateQueue = [NSMutableArray arrayWithCapacity:8];
}
return self;
}
- (void)dealloc
{
[requestQueue release];
[delegateQueue release];
[currentConnection cancel];
[currentConnection release];
[super dealloc];
}
- (void)startNextConnection
{
NSURLRequest *request = [requestQueue objectAtIndex:0];
currentConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate
{
[requestQueue addObject:request];
[delegateQueue addObject:delegate];
if ([requestQueue count] == 1)
[self startNextConnection];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveResponse: response];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveData: data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didFailWithError:error];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connectionDidFinishLoading: connection];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
#end
To use the connection queue, create an NSURLRequest instance and then call:
[[ConnectionQueue sharedInstance] queueRequest:request delegate:self];
There's no need to create the singleton instance of ConnectionQueue explicitly. It will be created automatically. However, to properly clean up, you should call [ConnectionQueue releaseShared] when the application quits, e.g. from applicationWillTerminate: of your application delegate.

UIActivityIndicatorView NSURLConnection, view not showing

I have a problem getting a UIActivityIndicatorView to show when I collect data from a server with help from the NSURLConnection request.
The request I think is asynchronous, i.e., started in a new thread. I have copied from Apple's AdvancedTableViewCells example. And I run it in XCode in the iOS 4.3 iPhone simulator. I have not tested it on a real iPhone yet.
Also I have googled this problem and tried a lot of suggestions but the feeling is that I have forgotten something basic. Below is my code from the class RootViewController.
I just select a row, create and add the activityview, startanimating, and then create the NSUrlConnection object which starts to fetch data from the server in another thread, I believe.
Any ideas?
#interface RootViewController : UITableViewController {
NSMutableData *receivedData;
UIActivityIndicatorView *activityView;
}
#end
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// In my rootviewcontroller
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:activityView];
[activityView startAnimating];
…
NSMutableURLRequest *tUrlRequest = [tQuery createUrlRequest:tStatId];
NSURLConnection *tConnectionResponse = [[NSURLConnection alloc] initWithRequest: tUrlRequest delegate: self];
if (!tConnectionResponse) {
NSLog(#"Failed to submit request");
} else {
NSLog(#"Request submitted");
receivedData = [[NSMutableData data] retain];
}
return;
}
...
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
NSXMLParser *tParser = [[NSXMLParser alloc] initWithData: receivedData];
...
[tParser parse];
...
[connection release];
[receivedData release];
[NSThread sleepForTimeInterval: 2.0]; // Just to see if activity view will show up...
NSUInteger row = 1;
if (row != NSNotFound)
{
// Create the view controller and initialize it with the
// next level of data.
VivadataTViewController *vivaViewController = [[VivadataTViewController alloc] init];
if (activityView != nil) {
[activityView stopAnimating];
}
}
}
Had the same exact issue, try to change the color of the UIActivityIndicatorView under Attributes Inspector -> Style to Gray

Resources