UITabelView doesn't get updated with new content from server side - iphone

I have an app which has an UITabelView which gets its content from a server side.In my app i read the content from the server each 30 seconds....for that I use a NSTimer.
This NSTimer gets initialized when I load the view which contains the UITableView and it is invalidated when I leave this view.
My problem is this:
if on the server side the content for the UITableView is updated with new item and the JSON received in the iphone app as a response to a request to the server contains that item....the UITableView that appears on the screen is still not updated.
Here is how I did it:
//the timer is started when this view is loaded and the method repeatServerRequest is called
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
if(playlistTimer == nil)
playlistTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target: self selector: #selector(repeatServerRequest) userInfo: nil repeats: YES];
}
//the method repeatServerRequest starts a new thread in the background which does a request //to server for downloadeding the content
- (void) repeatServerRequest{
[NSThread detachNewThreadSelector:#selector(backgroundThinking) toTarget:self withObject:nil];
}
- (void) backgroundThinking{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:#"a link to server"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
[pool release];
}
///when the response from server comes in these methods are called:
- (void)requestFinished:(ASIHTTPRequest *)request
{
[self performSelectorOnMainThread:#selector(didFindAnswer:) withObject:request waitUntilDone:YES];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"the value of error %#", error);
}
- (void) didFindAnswer:(ASIHTTPRequest *) request{
NSLog(#"update tabel");
SBJSON *parser = [[SBJSON alloc] init];
NSString *responseString = [request responseString];
NSArray *statuses = [parser objectWithString:responseString error:nil];
streams = [statuses valueForKey:#"_playLists"];
[parser release];
playList = [[NSMutableArray alloc] init];
idList = [[NSMutableArray alloc] init];
int ndx;
for (ndx = 0; ndx<streams.count; ndx++) {
NSDictionary *stream = (NSDictionary *)[streams objectAtIndex:ndx];
[playList addObject:[stream valueForKey:#"name"]];
[idList addObject:[stream valueForKey:#"id_playlist"]];
}
NSLog(#"playList %#", playList);
oneview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)];
tableViewPlaylist =[[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
tableViewPlaylist.bounces=NO;
tableViewPlaylist.backgroundColor=[UIColor clearColor];
[tableViewPlaylist setDelegate:self];
[tableViewPlaylist setDataSource:self];
}
So when I update the content on server side, the JSON thet I get as a response on server side is updated but the UITAbelView is not, UNLESS I RUN AGAIN MY APP.Any idea why?

Two main problems:
You are creating a new UITableView every time the server updates. This is unnecessary.
Even if you wanted a new tableView, you are not adding it as a subview of the main view.
What you should do instead is don't create a new tableView at all. After you've updated the data model call [tableView reloadData] on the main thread. Your table will update and all is good. Assuming you have everything configured correctly and it sounds like you do if you see the expected data when you launch the app.

Related

iOS 5 NSURLConnection with NSOperationQueue - Providing UI Feedback

I need to make multiple NSURLConnections to a JSON Web Service. I would like each WS call to keep in UI informed, probably with a UIActivityIndicatorView and label. So far I've created a NSURLConnection helper class to handle the connection and placed the URL delegates in the View. This works great for updating the UI with a single WS call.
For multiple calls, I'm trying to use an NSOperationQueue. I'd like to setMaxConcurrentOperationCount to one on the queue so that each Operation executes one at a time. Here's the relevant code on my View Controller:
ViewController.m
#import "URLOperationHelper.h"
#implementation ViewController
- (IBAction)showPopup:(id)sender
{
// Dictonary holds POST values
NSMutableDictionary *reqDic = [NSMutableDictionary dictionary];
// Populate POST key/value pairs
[reqDic setObject:#"pw" forKey:#"Password"];
[reqDic setObject:#"ur" forKey:#"UserName"];
operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
[operationQueue cancelAllOperations];
[operationQueue setSuspended:YES];
URLOperationHelper *wsCall1 = [[URLOperationHelper alloc] initWithURL:#"urlString1" postParameters:reqDic urlDelegate:self];
URLOperationHelper *wsCall2 = [[URLOperationHelper alloc] initWithURL:#"urlString2" postParameters:reqDic urlDelegate:self];
[operationQueue addOperation:wsCall1];
[operationQueue addOperation:wsCall2];
}
// Did the URL Connection receive a response
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Did receive response: %#", response);
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
// Handle status code here
webData = [[NSMutableData alloc]init];
}
// Did the URL Connection receive data
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Did receive data: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
assert(webData != nil);
[webData appendData:data];
}
// Did the connection fail with an error?
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"%#", error);
}
// Executes after a successful connection and data download
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Connection finished");
}
#end
And here is my URLOperationHelper.m
#implementation URLHelper
- (id)initWithURL:(NSString *)urlPath
postParameters:(NSMutableDictionary *)postParameters
urlParentDelegate:(id) pDelegate
{
if(self = [super init])
{
connectionURL = urlPath;
postParams = postParameters;
parentDelegate = pDelegate;
}
return self;
}
- (void)done
{
// Cancel the connection if present
if(urlConnection)
{
[urlConnection cancel];
urlConnection = nil;
}
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)cancel
{
// Possibly add an NSError Property
[self done];
}
- (void)start
{
// Make sure this operation starts on the main thread
if(![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
// Make sure that the operation executes
if(finished || [self isCancelled])
{
[self done];
return;
}
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self main];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)main
{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postParams options:NSJSONWritingPrettyPrinted error:&error];
// Convert dictionary to JSON
NSString *requestJSON = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"JSONRequest: %#", requestJSON);
// Declare Webservice URL, request, and return data
url = [[NSURL alloc] initWithString:connectionURL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[requestJSON UTF8String] length:[requestJSON length]];
// Build the request
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestData];
// Connect to Webservice
// Responses are handled in the delegates below
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:parentDelegate startImmediately:YES];
}
- (BOOL)isConcurrent
{
return YES;
}
- (BOOL)isExecuting
{
return executing;
}
-(BOOL)isFinished
{
return finished;
}
#end
The problem that I'm having is the Start method for the URLOperation is never called. The OperationQueue is created and the Operations are called, but nothing happens after that, execution or thread wise.
Also, is this a correct line of thinking to provide UI feedback using NSOperationQueues like this? I.E. calling the NSURLDelegates from the Operation?
If you set setSuspended to YES before adding the Operations then your Operations will be queued into a suspended queue.. i suggest not to suspend the queue at
furthermore, your operation never ends anyway. You need to assign the operation itself as the delegate and implement all necessary delegate methods. In these methods you can forward the messages to your parentDelegate and decide when you are finished and call your done method when appropriate (i suggest connection:didFailWithError: and connectionDidFinishLoading:)
There is a good tutorial here: http://blog.9mmedia.com/?p=549
You are also not completely implementing key-value-coding compilant properties correct. Whenever you call willChangeValueForKey: you also need to call didChangeValueForKey afterwards:
- (void)start
{
...
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
[self main];
}
and:
- (void)done
{
...
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
}
See this Q/A for KVC: when to use "willChangeValueForKey" and "didChangeValueForKey"?

iPhone -trouble with a loading data from webservice into a tableview

I am using a Window based application and then loading up my initial navigationview based controller in the appDelegate part - application didFinishLaunchingwithOptions (UPDATED)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
The method of getting the data from the webservice is usually triggered in this method of viewdidload .... in the [self getData]; method.
- (void)viewDidLoad
{
[super viewDidLoad];
if (_refreshHeaderView == nil) {
EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, self.tableView.bounds.size.height)];
view.delegate = self;
[self.tableView addSubview:view];
_refreshHeaderView = view;
[view release];
}
// update the last update date
[_refreshHeaderView refreshLastUpdatedDate];
[self loadImages];
HUDMB = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUDMB];
HUDMB.dimBackground = YES;
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUDMB.delegate = self;
HUDMB.labelText = #"Loading..";
[HUDMB show:TRUE];
[self getData];
[self.tableView reloadData];
}
Before loading it if the user is not registered/ does not have a credentials present then it takes the user to a login view controller .
- (void)loadView {
[super loadView];
if([Preferences isValid]?YES:NO)
{
}
else
{
int r = arc4random() % 5;
switch (r) {
case 0:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 1:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 2:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 4:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 3:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
default:
break;
}
}
}
I think the getdata function is making a lot of troubles. so let me add that and also the corresponding functions i use for data retrieval and serialization.
-(void)getData{
NSLog(#"loggin into call sheet page");
[self getCallSheetData];
NSLog(#"after call sheet");
}
- (void)getCallSheetData
{
NSString *postCMD = #"Blah... Blah... Blah...";
NSMutableData *postDataCMD = (NSMutableData *)[postCMD dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSURL *url = [NSURL URLWithString:[Preferences getURL]];
// NSLog(#"get call Sheet");
NSString *postLengthCMD = [NSString stringWithFormat:#"%d", [postDataCMD length]+1];
// NSLog(#"The CS String: %#",[Preferences retriveSession]);
requestCMD = [ASIHTTPRequest requestWithURL:url];
[requestCMD setURL:url];
[requestCMD addRequestHeader:#"Content-Length" value:postLengthCMD];
[requestCMD addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded"];
[requestCMD addRequestHeader:#"user-Agent" value:#"Mobile 1.4" ];
[requestCMD addRequestHeader:#"Content-Language" value:#"en-US"];
[requestCMD addRequestHeader:#"Accept-Encoding" value:#"gzip"];
[requestCMD addRequestHeader:#"Cookie" value:[Preferences retriveSession]];
[requestCMD setPostBody:postDataCMD];
[requestCMD setDelegate:self];
[requestCMD startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSMutableArray *CSArray = [[NSMutableArray alloc] init];
if( [[request responseString] isEqualToString:#"OK"]){
return;
}
// Use when fetching binary data
NSData *responseData = [request responseData];
NSDateFormatter *formatter1=[[NSDateFormatter alloc]init];
[formatter1 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[formatter1 setTimeZone:gmt];
NSDateFormatter *formatterFinal=[[NSDateFormatter alloc]init];
[formatterFinal setDateStyle:NSDateFormatterMediumStyle];
[formatterFinal setTimeStyle: NSDateFormatterShortStyle];
[formatterFinal setLocale:[NSLocale currentLocale]];
JSONDecoder *jCSArray = [[JSONDecoder alloc]init];
NSMutableArray *theObject = [jCSArray objectWithData:responseData];
// CallSheet= [NSMutableArray arrayWithCapacity:50];
for(id key in theObject)
{
csr = [[CallSheetRecord alloc] init];
// cName,cCompany,cId,cMemberId,crcStatus,crcTarget,cImportance,cLastContact
csr.importance = #"1";
csr.rcstatus = #"1";
csr.rcTarget = #"1";
csr.company = #"";
csr.lastContact= #"";
if([key valueForKey:#"firstName"] != Nil)
{
csr.name = [NSString stringWithFormat:#"%#",[key valueForKey:#"firstName"]] ;
if ([key valueForKey:#"lastName"] != Nil) {
csr.name = [csr.name stringByAppendingString:#" "];
csr.name = [csr.name stringByAppendingString:[NSString stringWithFormat:#"%#",[key valueForKey:#"lastName"]]];
}
}
if([key valueForKey:#"company"] != Nil)
{
csr.company = [NSString stringWithFormat:#"%#",[key valueForKey:#"company"]] ;
}
if([key valueForKey:#"memberId"] != Nil)
{
csr.memberId = [NSString stringWithFormat:#"%#",[key valueForKey:#"memberId"]] ;
}
if([key valueForKey:#"id"] != Nil)
{
csr.id_ = [NSString stringWithFormat:#"%#",[key valueForKey:#"id"]] ;
}
if([key valueForKey:#"lastContact"] != Nil)
{
NSDate *finalDate =[formatter1 dateFromString:[NSString stringWithFormat:#"%#",[key valueForKey:#"lastContact"]]];
//NSString *timeStamp = [formatter1 stringFromDate:[finalDate descriptionWithLocale:[NSLocale currentLocale]]];
//NSLog(#"Time stamp : %#",[finalDate descriptionWithLocale:[NSLocale currentLocale]]);
//NSLog(#"Time stamp : %#",timeStamp);
//csr.lastContact = [key valueForKey:#"lastContact"];
csr.lastContact = [formatterFinal stringFromDate:finalDate];
}
if([key valueForKey:#"importance"] != Nil)
{
csr.importance = [NSString stringWithFormat:#"%#",[key valueForKey:#"importance"]];
}
if([key valueForKey:#"rcStatus"] != Nil)
{
csr.rcstatus= [NSString stringWithFormat:#"%#",[key valueForKey:#"rcStatus"]] ;
}
if([key valueForKey:#"rcTarget"] != Nil)
{
csr.rcTarget = [NSString stringWithFormat:#"%#",[key valueForKey:#"rcTarget"]] ;
}
[CSArray addObject:csr];
}
CSD = [CSArray mutableCopy];
[CSArray release];
[formatter1 release];
[formatterFinal release];
//CallSheetArray = [CSArray mutableCopy];
//[csr release];
[jCSArray release];
[HUDMB hide:TRUE];
[self.tableView reloadData];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
UIAlertView *message = [[[UIAlertView alloc] initWithTitle:#"Hello World!"
message:[NSString stringWithFormat:#"%#",error]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil]autorelease];
[message show];
}
then right after that i try to load the table with [self getData]; that i get make using webservice call using asiHTTP .. for this question lets say it takes 3 seconds time to get the data and then deserialize it . now... my question is it works out okey in the later runs as I store the username and password in a seure location... but in the first instance.... i am not able to get the data to laod to the tableview... I have tried a lot of things...
1. Initially the data fetch methods was in a diffrent methods.. so i thought that might be the problem as then moved it the same place as the tbleviewController(navigationController)
2. I event put in the Reload data at the end of the functionality for the data parsing and deserialization... nothing happens.
3. The screen is black screen with nothing displayed on it for a good 5 seconds in the consecutive launches of the app.... so could we have something like a MBPorgressHUD implemented for the same.
could any one please help for these scenarios and guidance as to what paths to take from here. ...
Update:
Bounty Just answer 2 of the questions the best you can... The property was a thing i tried but it did not work... but my problem is not that... my problem is my screen is not able to load data from a webservice after i login until i do the "pull to refresh". Next eventualy starting my app takes about 5 seconds to show the screen(till that it shows a black screen)... what is the best way to show the apps Blank screen to the End- user when they make it to the app. I dont want the user to think the app is not working or has adversly affected their phone.
There are a number of potential issues here. The first is related to [self getData] - is this blocking or not?
1. getData is blocking (synchronous)
If this method is blocking (i.e. does not return immediately) then this will explain why you are not seeing anything on the screen for a few seconds. You are currently calling this method in the main thread, so it will stop the UI from being updated until is complete.
You can make it run in the background by doing this:
- (void) getDataInBackgroundThread
{
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
[self getData];
// force the table to redraw - UI operations must be on the main thread
[tableView performSelectorOnMainThread:#selector( reloadData )];
[pool drain];
}
In your main code, now instead of calling [self getData] call [self getDataInBackgroundThread];
2. getData is not blocking (asynchronous)
Even if getData is blocking, if it runs in the main thread it will still cause the UI to hang. Also, if it uses a lot of processor time it will slow down the UI and give the appearance of not running in the background.
To address this issue, you would need make sure the lengthy operation of getData really is not running in the main thread and also put in sleep() calls every so often to give some time for the UI to update.
You still haven't given enough info for people to really help, we're just guessing.
It is highly likely the delay is caused by getData. Try commenting that out or bracket it with NSLog statements to verify.
You should show us more code, specifically what is happening in getData and any delegate or notification handling implementations related to loading data.
You don't need to explicitly call [tableView reloadData] in viewDidLoad. The tableView automatically loads data the first time it is displayed.
As #dmatt said, if you are loading data asynchronously then getData would return immediately with no delay. You would typically would either respond to delegate messages or notifications (depending on how you are loading data) to reload the table when the data is finished loading and serializing.
There are lots of folks on SO who are happy to try and help you if given enough info. Especially when you offer a bounty.

iphone-uialertview-thred

in iphone,
i call one webservice For login checking...
When Application Is Underprocess ,, I Show UIAlertview With UIActivityIndicatorView
using thread,,
now i want to enable cancel button ,, means during the process if i want to cancel that process,, then my apps teminates webservice calling
but when i enable cancel button then ERROR OCccur,
Any One Can Help
My COde Is
-(NSMutableString*) getLoginMessage:(NSString*) UserName : (NSString *) Password
{
[NSThread detachNewThreadSelector:#selector(showAlertMethod) toTarget:self withObject:nil];
NSArray *Keys =[[NSArray alloc] initWithObjects:#"LoginName",#"PassWord",nil];
NSArray *KeyValue =[[NSArray alloc] initWithObjects:UserName,Password,nil];
operationName=[[NSString alloc] init];
operationName =#"ClientLogin";
NSString *StrService=[[NSUserDefaults standardUserDefaults] objectForKey:#"WebService"];
NSURL *WebServiceUrl=[WebServiceHelper generateWebServiceHTTPGetURL:StrService : operationName : Keys :KeyValue];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:WebServiceUrl];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser setDelegate:self];
[parser parse];
[Keys release];
[KeyValue release];
[StrService release];
[WebServiceUrl release];
//[parser release];
[NSThread detachNewThreadSelector:#selector(dismissAlertMethod) toTarget:self withObject:nil];
return Result;
}
-(void)showAlertMethod
{
NSAutoreleasePool *pool1=[[NSAutoreleasePool alloc]init];
progressAlert = [[UIAlertView alloc] initWithTitle:#"Loging in...\nPlease wait...\n" message:#"" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
CGRect alertFrame = progressAlert.frame;
UIActivityIndicatorView* activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.frame = CGRectMake(135,alertFrame.size.height+75, alertFrame.size.width,30);
activityIndicator.hidden = NO;
activityIndicator.contentMode = UIViewContentModeCenter;
[activityIndicator startAnimating];
[progressAlert addSubview:activityIndicator];
[activityIndicator release];
[progressAlert show];
[pool1 release];
}
-(void)dismissAlertMethod
{
NSAutoreleasePool *pool2=[[NSAutoreleasePool alloc]init];
[progressAlert dismissWithClickedButtonIndex:0 animated:YES];
[pool2 release];
}
There are some major flaws in how you attack the problem. Firstly you should not detach new threads to show and hide the alert view, all UIKit classes must be called form the main thread (only few documented exceptions exist).
What you want is an API designed to be asynchronous for dispatching the login request. I would suggest you use an Sync-Async pattern for this. I have written a longer blog post on this topic here: http://blog.jayway.com/2011/04/28/sync-asyn-pair-pattern-easy-concurrency-on-ios/
In essence I believe you want two public methods:
-(NSString*)loginMessageWithName:(NSString*)name
password:(NSString*)password
error:(NSError**)error;
-(NSOperation*)loginMessageWithName:(NSString*)name
password:(NSString*)password
delegate:(id<LoginMessageDelegate>)delegate;
The first method is synchronous, implement it as straightforward as you like, no threads on any thing, just make it work.
The second method is a wrapper that instantiates a NSOperation objects and puts it on some queue. Returning the operation allows you to cancel it, but the result will be returned on the delegate. The delegate will probably need to look something like this:
#protocol LogonMessageDelegate <NSObject>
-(void)didReceiveLoginMessage:(NSString*)message;
-(void)failedLoginMessageWithError:(NSError*)error;
#end
The implementation of loginMessageWithName:password:delegate: is very straight forward:
NSOperation* op = [[LoginMessageOperation alloc] initWithName:name
password:password
delegate:delegate];
[myOperationQueue addOperation:op];
return [op autorelease];
Most of the work will be done in your NSOperation subclass' main method. This is where you call the synchronious implementation, check for cancelation, and call back to the delegate if needed. Probably something like this:
-(void)main {
NSError* error = nil;
NSString* message = [logonMessageManager logonWithName:name
password:password:
error:&error];
if (![self isCancelled]) {
if (message) {
[delegate performSelectorOnMainThread:#selector(didReceiveLoginMessage:)
withObject:message
waitUntilDone:NO];
} else {
[delegate performSelectorOnMainThread:#selector(didReceiveLoginMessage:)
withObject:error
waitUntilDone:NO];
}
}
}
Then setup and handle the alert view on the main thread. Call [operation cancel] if the user cancels, or dismiss the alert when the delegate receives a callback.

xml parsing consumes time need efficent way on iphone

How to call main thread ??? i can parse but i cant display data
- (void)viewDidLoad {
//self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"10.png"]];
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(startTheBackgroundJob) toTarget:self withObject:nil];
}
- (void)startTheBackgroundJob {
NSUserDefaults *getida = [NSUserDefaults standardUserDefaults];
myIDa = [getida stringForKey:#"AppID"];
NSLog(#"#BOOK MARK ");
NSString *ubook = [[NSString alloc] initWithFormat:#"http://www.wapp=%#&action=show",myIDa];
NSLog(#" bookmark %#",ubook);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//NSString *outputString = [[NSString stringWithString:usearch] stringByAppendingString: UserText];
ubook = [ubook stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"My string is now = %#", ubook);
NSURL *url = [[[NSURL alloc] initWithString:ubook]autorelease];
//NSURL *url= [NSURL URLWithString:outputString];
NSLog(#" bookmark URL IS %#",url);
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
//Initialize the delegate.
XMLParserbookm *parser = [[[XMLParserbookm alloc] initXMLParser]autorelease];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
{
NSLog(#" xml parsed suucess");
//[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//[self searchTableView];
//mytimer4=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(wipe) userInfo:nil repeats:NO];
}
else{
NSLog(#"eeror");
}
[NSThread sleepForTimeInterval:3];
[self performSelectorOnMainThread:#selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO]; // HOW TO CALL MAIN THREAD
[pool release]
}
You can try with
viewDidAppear:, this method is called after you go to a new view. Then at least, you can switch to new view, you should make sure that there is something on the screen in waiting for the xml parsing
Using Thread: You put parsing into another thread and then callback your main thread after you finish, then there will be no block at all

Sharing data between two classes

this is my first iPhone application and I'm using JSON framework to decode JSON sent from a server.
I insert the data in a NSMutableArray from an AppDelegate file.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
responseData = [[NSMutableData data] retain];
museums = [[NSMutableArray alloc] init];
viewController = [[RootViewController alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://my_json_link"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed: %#", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSDictionary *data = (NSDictionary *)[json objectWithString:responseString error:&error];
[responseString release];
if (data == nil)
NSLog(#"JSON parsing failed: %#", [error localizedDescription]);
else {
for (NSDictionary *item in data) {
// Read the data
NSString *aName = [item objectForKey:#"name"];
NSString *aDescription = [item objectForKey:#"description"];
// Create a new museum object with the data from json
Museum *museum = [[Museum alloc] initWithName:aName description:aDescription];
// Add the museum object to the Array
[museums addObject:museum];
[museum release];
}
}
viewController.museums = museums;
}
The museums array is not empty inside connectionDidFinishLoading function, but I can't see it when I try to print it in RootViewController.
I tried to set the array in the line
viewController.museums = museums;
but I didn't understand what is wrong.
I can fix the problem only if I move these lines:
[window addSubview:viewController.view];
[window makeKeyAndVisible];
from the first function to connectionDidFinishLoading function. But in this case doesn't work the other view when I click one record of the table.
Thanks for any help.
viewController = [[RootViewController alloc] init];
First, I need you to make sure that the viewController you create in didFinishLaunching... is actually the correct viewController. Have you actually wired up that instance to be what you think it is or do you two instance os RootViewController?
Second if you are actually setting museums on the right instance of RootViewController you need to make sure that your timing is correct. This means that are you setting museums BEFORE you trying to print it out in viewController
--Edit--
OK since we established that things are happening in the wrong order you should try and reload the table. The UITableView has a method called reloadData that will take care of this for you and you need to call this everytime you change the data source after the table has been created.
So in RootViewController add a method called reload which in turn calls reloadData on your UITableView and modify your code:
viewController.museums = museums;
[viewController reload];
You could add to your view controller, temporarily just for debugging:
-(void) setMuseums:(NSMutableArray*)m {
self->_museums = [m retain];
}
and then add a breakpoint in there. Make sure it's getting hit, or maybe there's something later coming along and setting it to nil.
The Museums property is declared as #property (nonatomic, retain) NSMutableArray *museums; right?
Try to put a NSLog inside the for loop and check if it is executed.
try using
viewController.museums = [museums retain];