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?
Related
So I have some code like so:
#interface RequestHandler()
#property (nonatomic) NSInteger statusCode;
#end
#implementation RequestHandler
- (bool)sendRequest:(NSString *)surveyorId withData:(NSData *)requestData
{
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:true];
if (self.statusCode == 200)
{
return YES;
}
return NO;
}
Clearly the routine will carry on into the if-else statement before the request has finished. Therefore, self.statusCode is not set properly in the delegate didReceiveResponse before it is checked. What would be the best way of doing this?
I am just thinking of adding another bool property that will be set in connectionDidFinishLoading and then loop until this property is set. Once it has done that, then it will check self.statusCode. However I am thinking this will block the thread will it not? It will be no different from a sendSynchronousRequest right? Is there any way to do this without putting it into a background thread?
Instead of your sendRequest:withData: method returning a BOOL indicating success/failure, it would be better for your RequestHandler to have a delegate. It could then let its delegate know about the success/failure/whatever else when the asynchronous request has finished, instead of trying to return this information from the sendRequest:withData: method (which, as you've found out, doesn't work so well).
So, you could define you delegate protocol something like this (just as an example - you might want to include some more information in these):
#protocol RequestHandlerDelegate <NSObject>
- (void)requestHandlerSuccessfullyCompletedRequest:(RequestHandler *)sender;
- (void)requestHandlerFailedToCompletedRequest:(RequestHandler *)sender;
#end
Then, give your RequestHandler a delegate property of something that conforms to this protocol:
#property (nonatomic, weak) id<RequestHandlerDelegate> delegate;
(Make sure you set something as the delegate!)
Then, when your asynchronous request completes, you can send your delegate the appropriate message, e.g.:
[self.delegate requestHandlerSuccessfullyCompletedRequest:self];
You'll need to implement the NSURLConnection delegate methods in RequestHandler (from your code, I assume you've already done that), or, if your are targeting iOS 7+, you could take a look at NSURLSession instead.
You have to implement 2 delegate methods:
Status code: - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
Received data: - (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
Example usage:
Declaration
#interface RequestHandler : NSObject <NSURLConnectionDelegate>
{
NSMutableData *receivedData;
}
Request
- (void)sendRequest:(NSString *)surveyorId withData:(NSData *)requestData
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
// Apply params in http body
if (requestData) {
[request setHTTPBody:requestData];
}
[request setURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
Delegates
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *responseCode = (NSHTTPURLResponse *)response;
if ([self.delegate respondsToSelector:#selector(didReceiveResponseCode:)]) {
[self.delegate didReceiveResponseCode:responseCode];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
receivedData = [[NSMutableData alloc] initWithData:data];
if ([self.delegate respondsToSelector:#selector(connectionSucceedWithData:)]) {
[self.delegate connectionSucceedWithData:receivedData];
}
}
Instead of using NSURLConnection with delegate methods you can use NSURLConnection sendAsynchronousRequest block in your code. In the example you can check connection error and compare status codes.
NSURL *URL = [NSURL URLWithString:#"http://yourURLHere.com"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *rspreportStatus, NSData *datareportStatus, NSError *e)
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)rspreportStatus;
int code = [httpResponse statusCode];
if (e == nil && code == 200)
{
// SUCCESS
} else {
// NOT SUCCESS
}
}];
You can also check by logging this returnString.
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSArray *arrpicResult = [returnString JSONValue];
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
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
in my Program, I have a NSMutableData variable that collect the information from http://www.nhara.org/scored_races-2013.htm. After about the third time it gets information from a website, when it contains 90810 bytes, it either disappears or becomes null because if I print it a NSString, it is null. Here is the code
- (void)viewWillAppear:(BOOL)animated
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc] initWithCapacity:180000];
[self fetchEntries];
[super viewWillAppear:animated];
}
- (void)fetchEntries
{
// Construct a URL that will ask the service for what you want
NSURL *url = [NSURL URLWithString: #"http://www.nhara.org/scored_races-2013.htm"];//
// 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];
}
- (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];
NSLog(#"%#",xmlData);
NSString *xmlCheck = [[[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]autorelease];
NSLog(#"xmlCheck = %#", xmlCheck);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"error= %#",error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
// We are just checking to make sure we are getting the XML
NSString *xmlCheck = [[[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"xmlCheck2 = %#", xmlCheck);
}
What confuses me the most is that my NSMutableData stores data, but then loses it while claiming to have the same number of bytes.
Is there a constraint to the NSMutableData's size or is my problem just memory management?
You need to create a property for your xmlData variable. In your header file after your
#interface MyClass, make one like so
#property (nonatomic, retain) NSMutableData * xmlData;
If you are using ARC you leave it as strong if you using below ARC you change strong to retain. When you want to use your variable you do self.xmlData
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.