My MBProgress HUD and JSON approach - help/guidance to improve code - ios

I have working code but I need advice/direction if there is a better approach or if problems will arise using my current approach. The MBProgressHUD starts in the viewDidLoad, I then have a JSON method that posts and receives a response. It is a synchronous task because I need the information to modify labels on the screen. At the end of the JSON method is a call to stop the MBProgressHUD.
My viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
//some code missing
//start loading
MBProgressHUD * hud = [MBProgressHUD showHUDAddedTo: self.view animated:YES] ;
hud.labelText =#"Loading Information";
hud.detailsLabelText=#"Please wait.";
hud.dimBackground = YES;
}
My viewDidAppear:
-(void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:YES];
[self getJSON];
}
My getJSON method:
-(void) JSON{
//post
NSMutableString * postString = [NSMutableString stringWithString:homeUrl];
[postString appendString:[NSString stringWithFormat:#"?%#=%#",#"email",self.email]];
[postString appendString:[NSString stringWithFormat:#"&%#=%#",#"pass",self.pass]];
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:postString]];
[request setHTTPMethod:#"POST"];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
//get response
NSString * requestST = [[request URL] absoluteString];
NSData * jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:requestST]];
NSError *error;
//added check
if (jsonData!=nil) {
NSDictionary * dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
self.status = [dataDictionary objectForKey:#"status"];
self.balance = [dataDictionary objectForKey:#"result"];
//check status
if ([self.status isEqualToString:#"fail"]) {
NSLog(#"Fail")
}
else{
//assign variables
}
}
//if JSON is NIL
else{
NSLog(#"JSON Data is NIL");
}
//finish loading
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}
The architecture of the app is the following:
HOME-->Login-->Sign Up
Therefore I cannot use viewDidLoad in the Home method for the JSON call because it crashes. I uses popToRootViewController when there is a successful login. Just incase anyone ask why I use viewWillAppear for the JSON call. If there are any alternatives do not hesitate to suggest :)

It is a synchronous task because I need the information to modify labels on the screen.
That's not a good excuse. If you run synchronous networking code on the main thread, you block the UI and the user interface hangs.
In viewDidLoad, set up your view hierarchy with whatever user interface is appropriate to show that the content is loading. Then, when your JSON finishes loading, update the user interface to show the content.

You are using NSURLConnection so it's simple approach is
So start MBProgrssHUD in the NSURLConnection delegate method
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
and stop MBProgrssHUD in the NSURLConnection delegate method
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
and
- (void)connectionDidFinishLoading:(NSURLConnection *)connection

Related

Continue webservice call after transition from foreground to background in iOS objective C

Suppose I call a webservice when the app is in foreground. Now if the user sends the app to background then how do I make sure that this webservice call keeps executing in the background.
This is the piece of code that I am using in my app.
Login* login = [[Login alloc]init];
[login initiateSignInProcess];
initiateSignInProcess has 4 web service calls. they are normal
functions. I am using AFNetworking.
If any of the services fail, I call it again with a delay in the failure block of afnetworking code like below:-
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[self performSelector:#selector(getUserId) withObject:nil afterDelay:5];
}
Now I want to know that if the user sends the app to background, then how will the code execute? Will it call this function in bakcground till it succeeds?
Best to use Background Process for fetch. Here is great tutorial for solution [ http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Not possible in iOS6.x or lesser unless your application is has specific requirement to run in background like locations, Voip, music etc...
However this is possible with iOS7, please consider having a look at this
http://redth.codes/ios7-recipe-background-fetching/
**For(large FIle Downloade use Asynchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URL HERE"];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:myUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *myData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self startImmediately:YES];
**For(Small FIle Downloade use Synchronous Method)**
NSURL *myUrl = [NSURL URLWithString:#"Enter URl HERE"];
NSData *myData = [NSData dataWithContentsOfURL:myUrl];
UIImage *img = [UIImage imageWithData:myData];
add NSURLConnection Delegate in .h File
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[myData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[connection release];
//download finished - data is available in myData.
}
This is depends on OS scheduling whether it allows continue to run the services in background or kill it.
Best to use Background Fetch. Here is nice tutorial http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
Hope this solve your issue.

View is called before action is completed

I have a button used to download data and create a local db. Than, I want to open a table view with that data. It work but It seems the table view is called BEFORE the db cratio/actualization is made.
The code for the button is:
-(IBAction)downloadButton:(id)sender{
[controller dowloadingFunction]; //contains soem function based on NS
//the navigation controller to call the tableview
ViewController *tableController =[[ViewController alloc]init];
[self.navigationController pushViewController:tableController animated:NO];
}
the dowloadingFunction basically does:
{
...
// perform the reqeust
NSURLResponse *response;
NSError *myError = nil;
NSData *data = [NSURLConnection
sendSynchronousRequest: request
returningResponse: &response
error: &myError];
NSURLConnection *connectionPlatos = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connectionPlatos start];
}
when conection receive data I call "parseJson" function to parse and create data base.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d {
NSString *string = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
// NSLog(#"connection didReceiveData: %#",string);
jsonPlatos = string;
[self parseJson:jsonPlatos]; //<---- Funcion to parse and create data base.
}
My question is about how make that the creation of data base ends BEFORE navigation controller is called. Any help , please?

Saving Variable value outside of Connection didReceiveData Ùdelagate Method

I have tried to find a similar question but I was unable to do so. I have a singleton that is used to gather information through out my app. Once of the values is obtained within the following method.
-(NSString *)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data-(NSString *)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
In this method I get the response from th webservice that I am trying to connect to. Everything is good. However any attempt to save the variable value to my singleton fails. Within this method no problem. When I try to get to the next view and reference my singleton, it is as if the value never gets saved.
Here is a skeleton of my code.
NBXJSONResponse *jsonResponse = [NBXJSONResponse sharedHostedResponseSingleton];
[jsonResponse setHostedURL:_hostedURL];
Basically the goal here is to have the hosted payment page appear in the next view. The flow flow for that works. I just need to pass the URI link so that payment can be made.
I know that I can return a value from this delegate method, but since I never explicitly call this method, so I am unsure where it is being called. Probably the reason why my values are not being saved in the singleton class.
EDIT:
Right now I have one view which does all of the action.
//
// NBXViewController.h file
#import <UIKit/UIKit.h>
#interface NBXViewController : UIViewController
//*************************************************
// Variables to be declared for NBXViewController *
//*************************************************
#property (strong, nonatomic) IBOutlet UILabel *storeLabel;
#property (strong, nonatomic) IBOutlet UIButton *purchaseButton;
#property (strong, nonatomic) IBOutlet NSString *hostedURL;
//*************************************************
// Button Action methods for the buttons on View *
//*************************************************
- (IBAction)puchaseButtonAction:(id)sender;
//*************************************************
// Connection Delegate Methods *
//*************************************************
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; // Handle Connection Error
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; // Data Received from Connection (Header Information)
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; //Response from Connection (Return from Hosted being called)
-(void)connectionDidFinishLoading:(NSURLConnection *)connection; // Coonection did finish loading
#end
Implementation file.
//
// NBXViewController.m
// HostedAPI
//
// Created by Philip Teoli on 3/1/2014.
// Copyright (c) 2014 Philip Teoli. All rights reserved.
//
#import "NBXViewController.h"
#import "NBXJSONResponse.h"
#interface NBXViewController ()
#end
#implementation NBXViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//****************************************
// PurchaseButton Action Method *
//****************************************
- (IBAction)puchaseButtonAction:(id)sender
{
//************************************
// Building JSON Request *
//************************************
NSString *jsonString = #"Sample Request";
//**************************************
// Convert JSON String to NSData *
//**************************************
NSData* jsonRequestData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
//**************************************
// Creating JSON object to Send *
//**************************************
NSString *jsonRequest = [[NSString alloc] initWithData:jsonRequestData encoding:NSUTF8StringEncoding];
//*****************************************
// Base64 Encoding for Application Header *
//*****************************************
NSString *base64Signature = #"NbMIIkomZ8mFNw97EGRT:NAA5e965731e8a27ab11f5f";
NSString *basic = #"Basic: ";
NSData *base64SignatureData = [base64Signature dataUsingEncoding: NSUTF8StringEncoding];
NSString *base64SignatureEncoded = [base64SignatureData base64EncodedStringWithOptions:0];
NSString *header = nil;
header = [basic stringByAppendingString:base64SignatureEncoded];
// NSLog(#"Encoded Header: %#", header);
//*************************************
// Preparing to send XML through POST *
//*************************************
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[jsonRequest length]]; //Calculating the Content Length
NSData *postData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding]; // preapring JSON Request to be sent
//**************************************
// Headers and POST Body *
//**************************************
NSURL *serviceUrl = [NSURL URLWithString:#"https://pay.test.ewbservice.com/orders"];
NSMutableURLRequest *serviceRequest=[NSMutableURLRequest requestWithURL:serviceUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
[serviceRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[serviceRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[serviceRequest setValue:header forHTTPHeaderField:#"Authorization"];
[serviceRequest setHTTPMethod:#"POST"];
[serviceRequest setHTTPBody:postData];
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:serviceRequest
delegate:self startImmediately:YES];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];
NSLog(#"Hosted URL in Button: %#", _hostedURL);
}
//****************************************************
// Implementation of Connection Delegate Methods *
//****************************************************
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"did fail");
}
//*******************************************************
// Connection Data Received *
//*******************************************************
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString *dataResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData: [dataResponse dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
//********************************
// Iterating through Dictionary *
//********************************
NSArray *jsonLink = nil;
for(id key in jsonDictionary)
{
if ([key isEqualToString:#"link"])
{
jsonLink = [jsonDictionary objectForKey:key]; // this returns a dictionary. Will need to parse dictionary another 2 times to get proper URI.
}
}
NSDictionary *test = [jsonLink objectAtIndex:0];
for(id key in test)
{
if ([key isEqualToString:#"uri"])
{
_hostedURL = [test objectForKey:key];
}
}
NBXJSONResponse *jsonResponse = [NBXJSONResponse sharedHostedResponseSingleton];
[jsonResponse setHostedURL:_hostedURL];
}
//*******************************************************
// Connection Response Method *
//*******************************************************
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"did receive response: \n %#", response);
NBXJSONResponse *jsonResponse = [NBXJSONResponse sharedHostedResponseSingleton];
[jsonResponse setHostedURL:_hostedURL];
}
//*******************************************************
// Connection Finished Loading Data Method *
//*******************************************************
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"did finish loading!!! \n%#", _hostedURL);
// NBXJSONResponse *jsonResponse = [NBXJSONResponse sharedHostedResponseSingleton];
// [jsonResponse setHostedURL:_hostedURL];
}
#end
Please let me know of your thoughts. I can post more details if needed. Thanks in advance for your help!!!
Regards,
Thanks to the great help from #rdelmar I was able to get through this. Basically in short what was happening was that I was going to the next view before the information was received by my previous view from the web service. So when I was trying to change a label with a value to my Singleton, it was not stored yet. How I got around the issue is that when I click a button I wait until the web services responses and then I add a view to go to the new view. Now my URL that I was waiting reaches me, I save it to Singleton and then I go to the next view.
I will add my code once I can grab it and post it.
Thanks again for all of your help Eric!!! Your patience and help make you a great person on this community.
Regards,
Corporate One

NSURLConnection delegate methods

I am using a button action to update the value of a MySQL table field. The update is perform in the web server, but I need to update a UILabel text in my view Controller.
This is the code I have implemented:
- (IBAction)votarAction:(id)sender {
//URL definition where php file is hosted
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_async(backgroundQueue, ^{
int categoriaID = [[detalleDescription objectForKey:#"idEmpresa"] intValue];
NSString *string = [NSString stringWithFormat:#"%d", categoriaID];
NSLog(#"ID EMPRESA %#",string);
NSMutableString *ms = [[NSMutableString alloc] initWithString:#"http://mujercanariasigloxxi.appgestion.eu/app_php_files/cambiarvaloracionempresa.php?id="];
[ms appendString:string];
// URL request
NSLog(#"URL = %#",ms);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:ms]];
//URL connection to the internet
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:request delegate:self];
dispatch_async(dispatch_get_main_queue(), ^{
//update your label
});
});
}
#pragma NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//buffer is the object Of NSMutableData and it is global,so declare it in .h file
buffer = [NSMutableData data];
NSLog(#"ESTOY EN didReceiveResponse*********");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"ESTOY EN didReceiveDATA*********");
[buffer appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//Here You will get the whole data
NSLog(#"ESTOY EN didFINISHLOADING*********");
NSError *jsonParsingError = nil;
NSArray *array = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&jsonParsingError];
//And you can used this array
NSLog(#"ARRAY = %#",array);
//HERE LABEL.TEXT UPDATE CODE
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR de PARSING");
NSLog(#"ESTOY EN didFAILWITHERROR*********");
}
As I told you, the field value at the MySQL table is updated every time the button is tapped, but the problem is that the NSURLConnection delegate methods are never called.
Any help is welcome
In your view controller's header file add: <NSURLConnectionDelegate>
Also, there's no need to throw the NSURLConnection into a seperate background process, maybe that's why the delegates aren't called. NSURLConnection is already asynchronous
Perhaps try something like this:
- (IBAction)votarAction:(id)sender
{
int categoriaID = [[detalleDescription objectForKey:#"idEmpresa"] intValue];
NSString *originalString = [NSString stringWithFormat:#"%d", categoriaID];
NSMutableString *mutablesString = [[NSMutableString alloc] initWithString:#"http://mujercanariasigloxxi.appgestion.eu/app_php_files/cambiarvaloracionempresa.php?id="];
[mutableString appendString:originalString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:mutableString]];
request.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
request.timeoutInterval = 5.0;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (data)
{
NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^
{
// Update your label
self.label.text = [array objectAtIndex:someIndex];
});
}
else
{
// Tell user there's no internet or data failed
}
}];
}

Asynchronous calls not working in multithreaded section of code (MKOverlay canDraw)

I can successfully retrieve data asynchronously through NSURLConnection from any other part in the code base except in the canDrawMapRect function in my subclassed TileOverlayView class.
I'm modifying the MapKit sample called tileMap to download tiles from a server and overlay that information on the map. In the canDrawMapRect I call a function in the overlay class which in turn creates the url and opens up a connection. I have already tested my connection class and have confirmed that it does indeed work. I've run it in the init functions of overlay and overlayView with success. The urls are good too since I can throw them in a browser and they show the right pngs. I know that canDrawMapRect is running on multiple threads and I only have novice experience with threads.
Here is my connection code,
- (id)initWithStringUrl: (NSString*) url {
NSLog(#"Test Connect Init URL %#", url);
self = [super init];
if (self)
{
[self loadURL:[NSURL URLWithString:url]];
}
return self;
}
+ (UIImage*)connectSynchronousWithURL:(NSString*) url {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLResponse* response = [[NSURLResponse alloc] init];
NSError* error = [[NSError alloc] init];
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [UIImage imageWithData: data];
return image;
}
- (BOOL)loadURL:(NSURL *)inURL {
NSURLRequest *request = [NSURLRequest requestWithURL:inURL];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
if (conn) {
receivedData = [[NSMutableData data] retain];
NSLog(#"Connection Success");
} else {
NSLog(#"Connection Failed");
return FALSE;
}
return TRUE;
}
- (void)connection:(NSURLConnection *)conn didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveResponse");
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
NSLog(#"Succeeded! Received %d bytes of data", [receivedData length]);
}
Pretty standards stuff. If I run the code in the init of TileOverlayView it'll work just fine but if I run it in canDrawMapRect then none of the delegate functions get called. I suppose it's also worth mentioning that the synchronous connection to the server does work in the canDrawMapRect method. I don't get it at all T_T
Any help would be greatly appreciated. Thank you.
From the docs about NSURLConnection, this pretty much sums it up.
Note that these delegate methods will be called on the thread that started the asynchronous load operation for the associated NSURLConnection object.
Looks like I'll be needing to use CFRunLoopRun() and CFRunLoopStop(CFRunLoopGetCurrent()); to keep the thread alive. Or find an alternative to making these async calls in the thread.

Resources