How to pass NSURL object from one viewcontroller to another viewcontroller - ios

I have an NSURL object in ScanViewController class and pass it to the ListViewController class.
NSURL *url = [NSURL URLWithString:#"http:// some url"];
I am using xib's in my project and can't find an alternative for
[self.storyboard instantiateViewControllerWithIdentifier:]
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if(error || !data)
{
NSLog(#"JSON NOT posted");
}
else
{
NSLog(#"JSON data posted!");
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:Nil];
if([jsonObject respondsToSelector:#selector(objectForKey:)])
{
NSDictionary *dictionaryForUserID = [jsonObject valueForKey:#"ProjID"];
NSLog(#" Project Id = %#", dictionaryForUserID);
NSURL *urlToDisplayInListView = [NSURL URLWithString:[NSString stringWithFormat:#"http://some url/%#", dictionaryForUserID]]; **//Pass this object to other viewcontroller**
}
}
}];

I assume that you're performing a segue from one VC to another. If so, you can do this in your prepareForSegue:sender: method:
if ([segue.identifier isEqualToString:#"segueToListViewController"]) {
[(ListViewController *)segue.destinationViewController setURL:[NSURL URLWithString:#"http:// some url"]];
}
You have to declare a property in your destination VC to handle the URL, and you will need an accessor method to set that property.
EDIT
As danypata suggested, if you're not using segues, try the following
ListViewController *listViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ListViewControllerIdentifier"];
[listViewController setURL:[NSURL URLWithString:#"http:// some url"]];
[self presentViewController:listViewController animated:YES completion:nil];

Related

PrepareForSegue called before didSelectRowAtIndexPath

I am calling the following method in didSelectRowAtIndexPath.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
[self unreadMessageCounter];
}
In that method I am getting a value with parameter name "MsgCount". For that the written code is.
-(void) unreadMessageCounter{
NSUserDefaults *defaultUser=[NSUserDefaults standardUserDefaults];
NSString* username = [defaultUser objectForKey:KUserName];
NSString* password = [defaultUser objectForKey:KPassword];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeGradient];
});
NSString *url3 ;
NSString *base_url=[[NSUserDefaults standardUserDefaults] objectForKey:#"BASE_URL"];
url3=[[NSString alloc]initWithFormat:#"%#%#? username=%#&password=%#&deviceUniqueId=%#",base_url,MESSAGE_COUNTER,username,password,[defaultUser objectForKey:KDeviceToken]];
[defaultUser synchronize];
NSURL *requestURL = [NSURL URLWithString:[url3 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:requestURL];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *urlResponse, NSError *error) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)urlResponse;
NSLog(#"Response Code For Message Counter:: %ld", (long)[response statusCode]);
if(response){
NSMutableDictionary *returneDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"Return Dict For Message Counter:: %#", returneDict);
if (returneDict != nil) {
if ([returneDict valueForKey:#"valueSet"]){
for (NSDictionary *dict in [returneDict valueForKey:#"valueList"]) {
_counterNumber = dict[#"MsgCount"];
NSLog(#"counter number %#", _counterNumber);
}
}
else{
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
}
}
else{
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
}
}
}];
[task resume];
}
After that I am passing that _counterNumber string to the next view controller with the help of prepareForSegue, for that the following code is.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue.identifier isEqualToString:#"Show Notification"]) {
UINavigationController *nav = [segue destinationViewController];
WelcomeScreenViewController *welcomeScreenViewController = (WelcomeScreenViewController *)nav.topViewController;
welcomeScreenViewController.counterString = _counterNumber;
}
}
It was going to the next view controller but on the first call, after didselectrowatindexpath instead of unreadMessageCounter method, prepareForSegue is getting call that's why the _counterNumber value I am getting nil, but on the second time when I am calling then It is working as usual. So plese help me in that case because I am not getting any clue.
First Disconnect your segue from cell to nextViewController. And make new segue from current ViewController to your nextViewController like below screenshot
And after processing you API that you are calling in didSelectRowAtIndexPath perform segue through code like below.
[self performSegueWithIdentifier: #"Show Notification" sender: self];

Use NSArray object outside from json object

I'm new to Objective-C, just wondering how to use NSArray object outside from JSON.
For example:
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
}
NSLog(#"%#",myFinalListArray); //(This one showing all results..)
}];
NSLog(#"%#",myFinalListArray); //(This one giving empty result)
I have defined myFinalListArray and added objects in for loop.
If you use NSLog inside the loop or outside the loop it will show you results. But if I use this after }]; (after the code is ending.),
it's giving me empty array.
If you are accessing myFinalListArray in tableview then you can reload tableview inside the block after fetching data.
Or if you are accessing this array in some other task then you have to make notification call (have to add observer) and then post notification that will call some other method and access your array there and do your further stuff.
The block of code associated with sendAsynchronousRequest isn't executed until the network fetch has completed; this takes some time. While the network fetch is happening your code continues to execute, starting with the line immediately after sendAsynchronousRequest which is NSLog(#"%#",myFinalListArray); - but because the network operation hasn't completed you get an empty array.
In the block you need to include the code that you need to process the array, update your user interface or whatever (If you update UI make sure you dispatch the operation on the main thread)
This will work. You can try with this.
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
//Pass here the reference the a array. It will return you the array of you county when downloaded complete.
[self getURLResponse:&myFinalListArray];
NSLog(#"countryArray:%#",myFinalListArray);
}
-(void)getURLResponse:(NSMutableArray**)countryArray{
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
NSURLResponse *response;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:
request returningResponse:&response error:&error];
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
*countryArray = [[NSMutableArray alloc]initWithArray:myFinalListArray copyItems:YES];
}
-(void)sendRequest
{
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError) {
if (data.length > 0 && connectionError == nil)
{
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if( !myFinalListArray )
{
myFinalListArray=[[NSMutableArray alloc]init];
}
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
}
[self printArray];
}];
}
//create method that will execute after response
-(void) printArray
{
NSLog(#"%#",myFinalListArray); //(This one showing all results..)
}
Use
__block NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
This should work.
Happy Coding.
sendAsynchronousRequest runs asynchronously, meaning that the code below is already performed while the request is still running, so the NSLog is logging the empty array.
Only when the request finishes, the array is filled up but your outer NSLog was already performed.

How to pass data to the View Controller using asynchronous NSURLConnection

I have View Controller where I get data from web, parse Json, and pass string to another View Controller. If I use synchronous NSURLConnection, everything works just fine.
But if I switch to the asynchronous, then method (void)prepareForSegue:(UIStoryboardSegue *) calls before parsing Json data which I got from web.
Just jump over _jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil] method. Any thoughts? Thank you in advance for your help. Here is my code:
-(void)getClothInfo {
NSString *allowedClothSizeToServer = [_foreignSizeToServer stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *getDataURL = [NSString stringWithFormat:#"http://xsdcompany.com/jsoncloth.php?foreignSize=%#",allowedClothSizeToServer];
NSURL *url = [NSURL URLWithString:getDataURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: ^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
[self showAlertWithMessage2:#"Server is Unavialable"];
} else {
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Loop trough our jsonArray
for (int i=0; i<_jsonArray.count; i++) {
//Create our size object
_usSizeFromServer = [[_jsonArray objectAtIndex:i] objectForKey:#"usSizeCloth"];
}
}
}];
}
- (IBAction)getIt:(id)sender {
// Validate data
if ([self validData] == NO)
{
return;
}
[self getClothInfo];
[self showNextViewController];
}
-(void) showNextViewController {
[self performSegueWithIdentifier:#"GetCLothInfo" sender:nil];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
ResultViewController *resultViewController = [segue destinationViewController];
resultViewController.foreignSizeToResult = [[NSString alloc] initWithFormat:#"%# size for %# is %#", [_pickerProcessor selectedCountry].countryName, [_pickerProcessor selectedCloth].clothName, [_pickerProcessor selectedSize].sizeName];
resultViewController.dataForUsSize = [[NSString alloc] initWithFormat:#"Your US size for %# is %#", [_pickerProcessor selectedCloth].clothName, _usSizeFromServer];
}
You have two options. You could call showNextViewController from the completion block inside the getClothInfo method. Or better, add a completion block parameter to your getClothInfo method and call that from the completion block for the NSURLConnection.
Something like this:
-(void)getClothInfo:(void ^(void))completion {
NSString *allowedClothSizeToServer = [_foreignSizeToServer stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *getDataURL = [NSString stringWithFormat:#"http://xsdcompany.com/jsoncloth.php?foreignSize=%#",allowedClothSizeToServer];
NSURL *url = [NSURL URLWithString:getDataURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler: ^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
[self showAlertWithMessage2:#"Server is Unavialable"];
} else {
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Loop trough our jsonArray
for (int i=0; i<_jsonArray.count; i++) {
//Create our size object
_usSizeFromServer = [[_jsonArray objectAtIndex:i] objectForKey:#"usSizeCloth"];
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
}
}];
}
- (IBAction)getIt:(id)sender {
// Validate data
if ([self validData] == NO)
{
return;
}
[self getClothInfo:^ {
[self showNextViewController];
}];
}
It seems like you want your json data to be downloaded before you segue, in that case the synchronous NSURLConnection makes sense
When you make an asynchronous NSURLConnection call, it means that the subsequent code will be executed ( in this case the performSegue).
It would help if you could explain what your expected behavior is
Register for notification when response is obtained from the connection using
[[NSNotificationCenter defaultCenter] postNotificationName:#"ResponseObtained" object:_jsonArray];
in the second view controller add observer for notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleResponse:)
name:#"ResponseObtained"
object:nil];
You can access _jasonArray in handleResponse method with
- (void)handleResponse:(NSNotification *)notif{
NSDictionary *result = [notif object]; }

Not being able to pass webservice data to another viewcontroller after URL connection

I want to pass json data that I received from the following url and pass "username" data to SecondViewController. I only want to pass data, when is connection made and successful.
I am having following problems:
No matter URL is right or wrong, I am getting directed to SecondViewController. I only want to go to SecondViewController, when connection is successful and data has been fetched.
And I am not able to receive data on the SecondViewController when I put following line inside of URL connection code:
SecondViewController * destination=[segue destinationViewController];
destination.displayName=[info objectForKey:#"username"];
If I place following code right before, NSURL it works and I see the message in the SecondViewController. But I pass destination.displayName=#"Testing Message".
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSURL *url = [NSURL URLWithString:#"http://localhost/?user_id=1"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
//this is my second view controller
SecondViewController * destination=[segue destinationViewController];
destination.displayName=[info objectForKey:#"username"];
}//if connected
}];//end of connection
}//end of prepareforsegue
What is the best way to accomplish this? And is it right to place URL Connection code inside prepareForSegue?
I suggest this solution:
- (void)fetchRequest {
NSURL *url = [NSURL URLWithString:#"http://localhost/?user_id=1"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data.length > 0 && !connectionError) {
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
[self performSegueWithIdentifier:#"SecondViewController" sender:info];
}//if connected
}];//end of connection
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"SecondViewController"]) {
if ([sender isKindOfClass:[NSDictionary class]]) {
//this is my second view controller
SecondViewController * destination = [segue destinationViewController];
destination.displayName=[sender objectForKey:#"username"];
} // end of check on Sender Type
} // end identifier check ..
}//end of prepareforsegue
Good luck with your app.

Slow view switch using UIViewController

I'm working on my first app that integrates with a web service. Right now I have two views with their respective view controllers. I have a login view, which is where users will login to the app and I will verify and store there login credentials and I have a main view which shows all the users info from the web service. My two views work correctly individually however after verifying the credentials of the user on my login view I want to switch views to my login view. To do this I'm using UIViewController. Here is my code for when the login button is pressed:
-(IBAction)logIn:(id)sender{
//Show network activity is happening
UIApplication *application = [UIApplication sharedApplication];
application.networkActivityIndicatorVisible = YES;
//Validate credentials
[_loginNetworkingContorller checkCredentialsWithUsername:self.username.text withPassword:self.password.text completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(!error){
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
//if we get back a successful status code save username and password in keychain.
NSLog(#"SUCESS");
NSDictionary *credentials = #{self.username.text: self.password.text};
[KeychainUserPass save:#"INSERT APP NAME HERE" data:credentials];
NSLog(#"go to new page");
//print response from webservice for debugging purposes
NSMutableDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", jsonObject);
//switch to new view controller
UIViewController *mainController = [[RDMainViewController alloc] initWithNibName:#"RDMainViewController" bundle:nil];
[self.navigationController pushViewController:mainController animated:NO];
}
else{
//Error case, handle it.
NSLog(#"ERROR");
}
}
else{
//Error case, handle it.
NSLog(#"ERROR");
}
}];
}
And here checkCredentialsWithUsername method:
-(void)checkCredentialsWithUsername:(NSString *)username withPassword:(NSString *)password completionHandler:(void (^)(NSData *data,NSURLResponse *response, NSError *error))myCompletion
{
//Create request URL
NSString *requestString = #"WEB_SERVICE_URL";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
//Store password and user name for authentication
NSData *userPasswordData = [[NSString stringWithFormat:#"%#:%#", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
NSString *authString = [NSString stringWithFormat:#"Basic %#", base64EncodedCredential];
//Create an NSURL session
NSURLSessionConfiguration *sessionConfig=[NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.HTTPAdditionalHeaders=#{#"Authorization":authString};
self.session=[NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
myCompletion(data, response, error);
}];
[dataTask resume];
}
My issue is that it's taking an incredibly long time to switch views. Like sometimes over a minute. At first I thought it was the network connection but then I printed the data I was receiving from the web service and it was appearing very quickly. Even after I printed the data it was still taking a very long time for views to switch. I'm not really sure why but I think it has to do with the way I'm doing my blocks. Any idea why it's taking me such a long time to switch views?
Any help would be greatly appreciated.
#Danyun was right. I needed to do the following:
if (httpResp.statusCode == 200) {
//if we get back a successful status code save username and password in keychain.
NSLog(#"SUCESS");
NSDictionary *credentials = #{self.username.text: self.password.text};
[KeychainUserPass save:#"INSERT APP NAME HERE" data:credentials];
NSLog(#"go to new page");
//print response from webservice for debugging purposes
NSMutableDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", jsonObject);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//test to make sure we are on the main Queue for UI update
//switch to the new view controller
UIViewController *mainController = [[RDMainViewController alloc] initWithNibName:#"RDMainViewController" bundle:nil];
[self.navigationController pushViewController:mainController animated:NO];
}];
}

Resources