Weird error with NSJSONSerialization and UIPIckerView - ios

The way I have my program working is it pulls data down into the program via AFHTTPClient. Then I get my response data. I then take this data and parse it into a NSMutableArray via NSJSONSerialization. This array is used to populate a UIPickerView.
This method fires when the user opens the app or presses a refresh button. The problem is when the app opened, the call is made and I get the data back. If I go to the picker it seems empty but when you scroll down the bottom 2 of 5 are in there and when you scroll back up the others get in too. If at any point I press the refresh button the errors go away and the picker is properly populated. Any reason why it is not working properly? I reloadAllComponents after each call is made too.
-(void) getData{
// Paramaters
NSString *lat = #"40.435615";
NSString *lng = #"-79.987872";
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys: lat, #"lat", lng, #"lng", nil];
// posting the data and getting the response
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://mysite.com/"]];
[client postPath:#"/mypostpath.php" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *text = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", text);
NSError *error;
json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
// setting the first location to the text feild
NSDictionary* name = [json objectAtIndex:0];
locationField.text = [name objectForKey:#"name"];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", [error localizedDescription]);
}];
// reload the picker
[pickerCust reloadAllComponents];
// setting the first location to the text feild
NSDictionary* name = [json objectAtIndex:0];
locationField.text = [name objectForKey:#"name"];
}

The problem is because the [pickerCust reloadAllComponents]; is being called before the block that loads the data is finished. Move the call into the success block.
Since it interacts with a UI component, wrap it in an dispatch_async so it runs on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
[pickerCust reloadAllComponents];
});

Related

send specific parameters in response to server from next view controller using AFNetworking 3.0?

i'm very new in iOS app development.I am going to use AFNetworking 3.0. On first view when i enter mobile no. and click on submit button then it connect with server and i get following response from server:
responseData: [{"edriverId":"bd307a3ec329e10a2cff8fb87480823da114f8f4","token":"6uc4d1houfecbmjgy9ezpru9n25nw40b17cwk439j52"}]
now my question is,how can i take only that token from responseData and send it to server from next view. Please help me.
NOTE:whenever we call service token changes every time.
my code is:
-(void)serverconnection
{
NSString *Loginurl = [NSString stringWithFormat:#"https://24x7tracker.com/busservices/School/DriverPreLogin"];
NSDictionary *params = #{#"mobile":self.phonenumber.text,
#"archive":#"schooldb1"
};
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
AFSecurityPolicy* policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[policy setValidatesDomainName:NO];
[policy setAllowInvalidCertificates:YES];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript",#"text/html", nil];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json",#"text/html",nil];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json",#"text/plain",nil];
[manager POST:Loginurl parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"Json: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
[self getdata:responseObject];
NSString *str = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"responseData: %#", str);
[self performSegueWithIdentifier:#"loginsegue" sender:self];
}
failure:^(NSURLSessionTask *operation, NSError *error)
{
NSLog(#"Error: %#", error);
UIAlertController *Erroralert= [UIAlertController
alertControllerWithTitle:#" Network Connection Failed!!"
message:#"Please try again"
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:Erroralert animated:YES completion:nil];
UIAlertAction* yesButton = [UIAlertAction
actionWithTitle:#"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[self resignFirstResponder];
[Erroralert dismissViewControllerAnimated:YES completion:nil];
}];
[Erroralert addAction: yesButton];
}];
}
-(void)getdata:(NSData *)data
{}
you can do this in two ways
NSUserDefault
// NSArray *temp = (NSArray *)responseObject;
NSError *error;
NSArray *temp = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
[[NSUserDefaults standardUserDefaults] setObject:[[temp objectAtIndex:0] valueForKey:#"token"] forKey:#"token"];
[self performSegueWithIdentifier:#"loginsegue" sender:self];
on second Page
Retrieve
for Retrieve the String in NSUserDefaults you can directly use objectForKey
NSString *token = [[NSUserDefaults standardUserDefaults]objectForKey:#"token"];
Pass the string to second Page like
first page create one global value for store the object in string
#property (strong, nonatomic) NSString *tokenString;
serilize the response object and store in the global value which one you need
NSError *error;
NSArray *temp = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
// in this line we assingn the current value to global value
tokenString = [[temp objectAtIndex:0] valueForKey:#"token"];
[self performSegueWithIdentifier:#"loginsegue" sender:self];
on that destination view controller create one global value for pass the current string
#property (strong, nonatomic) NSString *fetchtokenString;
on that first VC the prepareForSegue method pass the globalized value to second page
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
secondViewController *vc = segue.destinationViewController;
// pass the current object to second page value
vc.fetchtokenString = tokenString;
finally second VC you can get the value on viewDidload page
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog (#"token value ==%#",fetchtokenString);
}
You can do as follow
Parse ur response like this
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject];
Get your token in one global variable suppose
NSString *token = [json objectForkey:#"token"];
When you push next UIViewController, set this value as property of next UIViewController. For that, you need to create one property of type NSString in next UIViewController like:
#property(nonatomic, copy) NSString *token;
Access that propery in first UIViewController like this.
You have to add following line before pushing the next UIViewControler
sectionViewControllerObject.token = token //variable of first UIViewController
#Anbu.Karthik if you want to send the value from First view controller to Second view controller then you can do in many of ways either you can use segue or delegate or NSnotification.
I am doing in swift for only getting the token data just try this using objective C.
var myArray: NSMutableArray = [["edriverId":"1234","token":"anytokenid"]]
print(myArray)
var parsedata:String = myArray.objectAtIndex(0).valueForKey("token")as!String
print(parsedata)

AFNetworking request returns nil

i have a request which returns information from a php web service. I'm having trouble adding this to a array which can be used in my UICollectionView. It seems like whatever i do i cant return the data. I think it is because i'm returning the array before i've added any objects. I've tried placing the NSLog several places, but without luck. What am i doing wrong?
When i place this NSLog(#"%d", imagesArray.count); beyond the request it returns 0.
ViewDidAppear:
-(void)viewDidAppear:(BOOL)animated {
imagesArray = [[NSMutableArray alloc] init];
[self getImages];
}
getImages method:
-(void)getImages {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:#"http://URL.COM" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseObject options:kNilOptions error:nil];
NSString *theTitle = [[json objectForKey:#"response"] valueForKey:#"title"];
NSString *theUrl = [[json objectForKey:#"response"] valueForKey:#"imageUrl"];
[imagesArray addObject:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
theTitle, #"title",
theUrl, #"url",
nil]];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
NSLog(#"%d", imagesArray.count);
}
AFNetworking is Asynchronous, you have to place the log inside the success block. Otherwise the array will always be empty.
One good solution would be to pass a block to your getImages function like that
-(void) getImages:(void (^)(BOOL result))callback {
// your code here then you call callback(YES or NO) inside your success or failure block.
}
[self getImages:^(BOOL result){
if(result)
//we got the images, we can now display them etc.
}];

What am I doing wrong with the Pocket API for Objective-C that's causing archive commands to fail?

I'm really scratching my head at this one. I'm using the Pocket API to allow users to archive Pocket articles from my app, but whenever I try to do so with the below code I get this error:
Error Domain=PocketSDK Code=400 "Invalid request, please refer to API documentation" UserInfo=0xc17d3b0 {NSLocalizedDescription=Invalid request, please refer to API documentation}
Code:
NSDictionary *arguments = #{#"action": #"archive",
#"item_id": articleID};
[[PocketAPI sharedAPI] callAPIMethod:#"send" withHTTPMethod:PocketAPIHTTPMethodPOST arguments:arguments handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error) {
if (!error) {
NSLog(#"Archived article.");
}
}];
Exactly what part of that is incorrect? Am I not POSTing a send method to the API?
EDIT: I even changed it to have #"action" be #"actions" and to supply it the above NSDictionary, and it returns without an error but doesn't affect it on the Pocket website...
EDIT 2: Per the response of Joseph Chen I changed my code to the following:
// Create data to pass to the Pocket API (a JSON array of actions)
NSError *error;
NSArray *actions = #[#{#"action": #"archive",
#"item_id": articleID}];
NSData *actionsAsJSONData = [NSJSONSerialization dataWithJSONObject:actions options:kNilOptions error:&error];
NSString *actionsAsJSONString = [[NSString alloc] initWithData:actionsAsJSONData encoding:NSUTF8StringEncoding];
NSDictionary *arguments = #{#"actions": actionsAsJSONString};
[[PocketAPI sharedAPI] callAPIMethod:#"send" withHTTPMethod:PocketAPIHTTPMethodPOST arguments:arguments handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error) {
if (!error) {
NSLog(#"%#", response);
}
else {
NSLog(#"%#", error);
}
}];
Which returns:
action_results" = (
1
);
status = 1;
Yet when I go to the website and log in, the article I "archived" is still staring me in the face, unarchived.
According to the documentation the actions parameter should be a JSON dictionary. So you could either...
Create the JSON dictionary manually:
NSString *jsonString = [NSString stringWithFormat:#"[{\"action\":\"archive\",\"item_id\":\"%#\"}]", articleID]; // articleID is a NSString?
NSDictionary *arguments = #{#"actions": jsonString};
Use NSJSONSerialization:
NSDictionary *actions = #{#"action": #"archive", #"item_id": articleID};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:actions
options:kNilOptions
error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSDictionary *arguments = #{#"actions": jsonString};
This answer is also a reference.
Here's the code taken (almost) straight from my app:
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
NSDictionary *arguments = #{#"actions" : #[#{#"action" : #"archive",
#"item_id" : itemId,
#"time" : [NSString stringWithFormat:#"%ld", (long)timestamp]}]};
[self.pocketAPI callAPIMethod:#"send"
withHTTPMethod:PocketAPIHTTPMethodPOST
arguments:arguments
handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error)
{
if (!error) {
// OK
} else {
// handle error
}
}];

NSMutableArray appears empty but is being populated

Am having a major problem with an NSMutableArray, I'm probably missing something really obvious - have probably been looking at it so much that I just can't see it.
Am reading some Tweets and then using the results to populate an NSMutableArray :
#synthesize testArray;
- (void)viewDidLoad{
[super viewDidLoad];
testArray = [[NSMutableArray alloc] init];
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.jsonq=kennedy&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200) {
// The response from Twitter is in JSON format
// Move the response into a dictionary
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSArray *results = [dict objectForKey:#"results"];
//Loop through the results
for (NSDictionary *tweet in results) {
tweetStore *tweetDetails = [[tweetStore alloc] init];
NSString *twitText = [tweet objectForKey:#"text"];
//Save the tweet to the twitterText array
tweetDetails.name = #"test";
tweetDetails.date = #"test";
tweetDetails.tweet = twitText;
[testArray addObject:tweetDetails];
}
tweetStore *retrieveTweet = (tweetStore*)[testArray objectAtIndex:0];
NSLog(#"tweet is: %#", retrieveTweet.tweet);
//NSLog(#"Array is: %#", testArray); - *can* view Array etc here
}
else {
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}
}
];
NSLog(#"test array: %#", testArray); //Array is now empty......
}
I can view the array within the code where I've marked it, retrieve the objects I want etc, it works perfectly. But as soon as I try to access or display anything from the array outside of this then it seems to be empty. Any help or pointers would be appreciated.
The problem is that the code in the block is performed in an asynchronous way, so it is executed after the NSLog code.
The code executed follows this flow:
First you perform the initialization:
[super viewDidLoad];
testArray = [[NSMutableArray alloc] init];
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.jsonq=kennedy&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
You start the request:
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { <block_code> } ];
You print array information and it is empty (correct):
NSLog(#"test array: %#", testArray);
Now the asynchronous request ends and the code in the block is executed: so the array is populated correctly only in the block code.

Getting tweets via TWRequest for a tableview

Ok, I'm pretty new to this and been struggling with what you guys may feel is quite an easy exercise. I have trawled high and low and cannot find a good tutorial or walkthrough on how to do this. Basically, I am using the code below to obtain tweets and I only want the 'text' part of the tweet. How do I extract it out of the NSDictionary in order to use the 'text' key in a tableview? I have tried [dict objectForKey:#"text"] but it does not work - 'dict' does not seem to contain a 'text' attribute. Thanks in advance for any help.
// Do a simple search, using the Twitter API
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
#"http://search.twitter.com/search.json?q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSLog(#"Twitter response: %#", dict);
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
Yes there is an objectForKey#"text" but it is an array which means that every entry (tweet) has text (and several other attributes). So we've to loop through the tweets to get FOR every tweet the text.
In your .h file
NSMutableArray *twitterText;
In your .m file
Do this somewhere in viewdidload
twitterText = [[NSMutableArray alloc] init];
And now we can loop through your results. Paste this code where you've NSLog(#"Twitter response: %#", dict);
NSArray *results = [dict objectForKey#"results"];
//Loop through the results
for (NSDictionary *tweet in results)
{
// Get the tweet
NSString *twittext = [tweet objectForKey:#"text"];
// Save the tweet to the twitterText array
[twitterText addObject:(twittext)];
And for your cells in your tableView
cell.textLabel.text = [twitterText objectAtIndex:indexPath.row];
I think that should be working.

Resources