How to implement loader untill work in progress? - objective-c

I am new to ios Development. I need to call api to fetch information from web,in controller 2 i am calling my api to show information from web and from controller 1's button action i am calling controller 2. I implemented custom loader in controller 2 's view did load method in processing time but i don't know why it takes so much time to go controller 2 from controller 1 and i don't know how to reduce that processing time or can u please tell me can i implement loader in button action's processing time. need help.
to call api-
NSURLRequest *theRequest=[NSURLRequest requestWithURL:getAllClassifications cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSData *returnData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:nil];
// NSLog(#"return id =%#",returnData);
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:returnData
options:kNilOptions
error:&error];
//NSDictionary *location = [json objectForKey:#"npidata"];
NSArray *latestLoans = [json objectForKey:#"taxonomy"];
recipes = [latestLoans valueForKey:#"classification"];
firstname = [latestLoans valueForKey:#"classification"];
to implement custom loader-
NSURL *url = [[NSBundle mainBundle] URLForResource:#"loadingg" withExtension:#"gif"];
self.loader.image = [UIImage animatedImageWithAnimatedGIFData:[NSData dataWithContentsOfURL:url]];
float progress = 0.0f;
while (progress < 1.0f) {
progress += 0.01f;
HUD.progress = progress;
usleep(50000);
}
[NSTimer scheduledTimerWithTimeInterval:progress target:self selector:#selector(abcd) userInfo:nil repeats:NO];

#jrturton seems to have it right, you are doing NSURLConnection sendSynchronousRequest which will stop dead until all the data is returned. You will find the system more responsive using an asynchronous request.
NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately: true];
That then returns immediately to your code before the data is ready. later, it will call back to the delegate methods you also have to define, to give you the data as it comes in. There is a simple tutorial at http://codewithchris.com/tutorial-how-to-use-ios-nsurlconnection-by-example/ and the extensive details at the apple documentation

Related

View Controller Segue Delay after NSURLConnection

So this is my first post, I've found this site incredibly informative in my brief history with Objective C and iOS programming. Anyhow, I've run into a problem of sorts. A quick summary: I'm attempting to write a login form, which uses calls a custom class that with hit a webserver to auth using NSURLConnection. I'm using protocols and delegates to delegate back to the calling class to perform a segue to the main menu view controller once the authentication is complete.
The problem is that the menu I'm attempting to segue into takes anywhere from 6 to 75 seconds to display. If I remove the API call, it loads immediately. However, I'm doing logging throughout the process, and everything appears to step through at a normal pace. I even log when the menu view controller is loaded, and all logging happens normally. But the actual display of the menu is delayed!
Here are some code details:
View Controller Methods:
- (void) userLogin:(NSString *)userName password:(NSString *)password {
NSLog(#"VC login method");
api = [theAPI getSelf];
[api setDelegate:self];
[api userLogin:userName password:password];
}
- (void) userLoginDone:(BOOL)successful {
[self performSegueWithIdentifier:#"sgLoginToMainMenu" sender:self];
NSLog(#"Login Done");
}
API Method:
- (void) userLogin:(NSString *)userName password:(NSString *)password {
NSURL *url = [NSURL URLWithString:(NSString *) [API_PATH stringByAppendingString:#"test.html"]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSDictionary *json = [[JSON new] parseJSON:data];
self.usrID = [json objectForKey:#"usrID"];
self.sessionID = [json objectForKey:#"sessionID"];
self.userName = [json objectForKey:#"Username"];
NSLog(#"Username: %#", [json objectForKey:#"Username"]);
[[self delegate] userLoginDone:YES];
}];
}
All the NSLogs execute in a normal timespan (few milliseconds). Yet the main menu view controller takes entirely too long to appear! I'm very new to iOS programming, so I'm hoping I'm just overlooking something that googling couldn't solve. Any help would be greatly appreciated!
You need to update the UI on the main thread, but userLoginDone: is being called on an NSOperationQueue, which create its own separate thread. This could explain the delay in displaying. Have you tried using [NSOperationQueue mainQueue] (which returns the queue associated with the main thread) to pass to sendAsynchronousRequest: instead?

iOS and Objective-C: How to keep an object globally

Developing an app for iOS, I need to know how to have instanced and available an object created when user authenticates.
I am using OAuth2 method properly implementing gtm-oauth2 framework. The user entries, sees the login form displayed in a web view and correctly authenticates. In that moment, as detailed in the documentation, I go like this:
if (error != nil){
// Do whatever to control the error
}
else
{
// Authentication succeeded
// Assign the access token to the instance property for later use
self.accessToken = myAuth.accessToken;
[myAuth setShouldAuthorizeAllRequests:YES];
[self setAuth:myAuth];
// Display the access token to the user
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Authorization Succeeded"
message:[NSString stringWithFormat:#"Access Token: %#", myAuth.accessToken]
delegate:self
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
Later, in the same controller, I use the self.auth object like this to access my API once the user has authenticated:
[request setURL:getCartsURL];
[request setValue:self.accessToken forHTTPHeaderField:#"Authorization"];
[self.auth authorizeRequest:request
completionHandler:^(NSError *error) {
NSString *output = nil;
if (error) {
output = [error description];
} else {
// Synchronous fetches like this are a really bad idea in Cocoa applications
//
// For a very easy async alternative, we could use GTMHTTPFetcher
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (data) {
// API fetch succeeded
output = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
SBJsonParser *jsonParser = [SBJsonParser new];
// Parse the JSON into an Object
id parsed = [jsonParser objectWithString:output];
NSArray *arrayResponse = [[NSArray alloc] initWithArray:parsed];
} else {
// fetch failed
output = [error description];
}
}
}];
So far, I have been using a local instance of self.auth object, what happens to be insufficient if I want to have that object globally accessed from any point of the whole app. Ok for the init view controller, but not for the whole app.
I think I can somehow access this first view controller to get the object anytime I want it. But I guess we have better methods to have it globally instanced and accessible from any point of the app.
Can you please help me with this?
Thanks a lot.
You should use a Singleton. Here is a nice article on how to set one up.
You could change the [self setAuth:myAuth]; of that ViewController to set an object on the AppDelegate. Create it there and set it, then you'll be able to access it from anywhere.
[[UIApplication sharedApplication] delegate] will give you a pointer to your app delegate, the one that was automatically created when you made the project.

objective-c: returning data from server

The following are methods that I am using to retrieve data from a server while displaying a UIActivityIndicator. I'm trying to put these methods in the app delegate and then call them from other classes, but I don't know how to return my JSONData. Can anybody help with this?
-(void)startProcess:(NSString *)buildURL{
UIActivityIndicatorView *aInd = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActionSheetStyleBlackTranslucent];
[aInd setFrame:CGRectMake(0, 0, 50, 50)];
[aInd startAnimating];
// then call the timeCOnsumingmethod in separate thread.
[NSThread detachNewThreadSelector:#selector(getData:) toTarget:self withObject:buildURL];
}
- (void)getData:(NSString *)buildURL{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Query our database for a restaurant's menus
NSURL *url = [NSURL URLWithString:buildURL];
NSError *e;
NSString *jsonreturn = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&e];
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
// NSError *error = nil;
[self performSelectorOnMainThread:#selector(endProcess:) withObject:jsonData waitUntilDone:YES];
[pool release];
//return jsonData;
}
- (IBAction)endProcess:(NSData *)jsonData{
// ??????????
return jsonData;
}
Not sure why got downvoted but your approach is all wrong. Here's what you want to do:
Add the UIActivityIndicatorView
Use NSURLConnection to asynchronously retrieve the data
Use NSJSONSerialization to decode the received JSON into a NSDictionary or NSArray
Remove the UIActivityIndicatorView
Your best bet would be to implement this as a separate class that takes a delegate object. You could implement a delegate protocol to indicate states like 'started network activity' (which your delegate could use to add a spinner view), and 'received data' (which would pass the decoded object back to the delegate - the delegate could then remove the spinner).
One of the benefits of this approach is you can easily set it up so that the connection/request is canceled when the object deallocs. Then you just store the request object as a property on your delegate, and when your delegate goes away, it deallocs the request, which cancels/cleans up properly.

How to update MBProgressHud with AFHTTPRequestOperation

I downloaded some examples in order to learn Ios.
The examples include:
AFNetworking;
Inapp purchase (from RayW);
MBProgressHud;
In my viewcontroller i push a UIbutton which triggers the Inapp purchase Singleton example and start download a file from my server with AFHTTPRequestOperation. This part of communication works. But what i want to achieve is to have my hud updated while downloading. As the file is >10Mb.
So, the question is how do i update the hud with the progress of the download? I try to draw it down.
I push the button in Viewcontroller and the hud will displayed;
--> request will sent to the Singleton InApp helper class which handles the networking part;
--> After that the AFHTTPRequestOperation will be called inside the singleton class for the download of file;
---> During this download i use the setDownloadProgressBlock method for the progress.
But how do i sent the progress info back to my hud in the viewcontroller?
Thanks.
This is what I did with a similar problem, following #mattt advice.
My Singleton InApp helper has productDownloadURL ivar and a prepareForDownload method that returns an AFHTTPRequestOperation to the caller:
- (AFHTTPRequestOperation * )prepareForDownload:(NSString *)productIdentifier
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:_productDownloadURL]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:productIdentifier];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
return operation;
}
My RootViewController make the request by using AFHTTPRequestOperation and sets downloadProgress/success/failure blocks as below:
AFHTTPRequestOperation *operation = [[InAppRageIAPHelper sharedHelper] prepareForDownload:productIdentifier];
[operation setDownloadProgressBlock:^(NSInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead) {
float percentDone = ((float)((int)totalBytesRead) / (float)((int)totalBytesExpectedToRead));
[(UIProgressView *)_hud.customView setProgress:percentDone];
_hud.labelText = [NSString stringWithFormat:#"%f",percentDone];
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
_hud.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"success.png"]];
[self performSelector:#selector(dismissHUD:) withObject:nil afterDelay:1.5];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
_hud.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"error.png"]];
}];
[operation start];
hud is a MBProgressHUD. You could also enhance the progress display using MBProgressHUDModeDeterminate mode.
Make the request from a controller, and keep a reference to the operation when you create and enqueue it in a variable (you can do this by creating an intermediary operation object with HTTPOperationWithRequest:success:failure, and manually doing enqueueHTTPOperation:.
In the body ofsetDownloadProgressBlock, set the progress property of the progress view (you need to divide bytesReceived by bytesExpectedToReceive in order to normalize between 0.0 and 1.0.

how to make a small transparent modal overal to signify loading data?

I'm sure this has been asked before, but I've had no luck finding it. In my app data is loading synchronously, which locks up the app. I've tried asynch loading, but that doesn't work with the JSON parser.
To denote that the app isn't frozen, just working on downloading data, I was hoping to present the user with a small transparent overlay with the loading icon. I was wondering how to go about this - do I need to put it on another thread?
To clarify, I want to do something very similar to the Netflix iPad app - their loading overlay is perfect for the projet I'm working on.
Edit: I've added some async code below
I first call this function:
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSURLConnection *c = [[NSURLConnection alloc] init];
[self connectionWorks:c didReceiveData:data];
connectionworks
-(void)connectionWorks:(NSURLConnection *)connection didReceiveData:(NSData *)data{
OLWork *newWork;
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *results = [jsonString JSONValue];
NSArray *rawBooks = [results objectForKey:#"works"];
for (NSDictionary *work in rawBooks) {
newWork = [[OLWork alloc] init];
newWork.title = [work objectForKey:#"title"];
newWork.author = [[[work objectForKey:#"authors"] objectAtIndex:0] objectForKey:#"name"];
newWork.key = [work objectForKey:#"key"];
[self.works setValue:newWork forKey:newWork.title];
}
}
This will do the job for you, it's well documented and easy to use
https://github.com/jdg/MBProgressHUD
Out of intrest which JSON parser are you using? Getting asynchronous requests working would be a much better solution.