authorizeRequest oAuth not called from another class? - ios

I'm currently using Oauth2SampleTouch by Google, so people can log-in with their google accounts into my app. However whenever I call a method from The SampleRootViewController it doesn't go through the authorizeRequest method (only if I call it from another class.).
Here's the method in SampleRootViewController that I'm calling form another class.(the user is already logged in by this time)
-(NSString *)hasLikedVideo:(NSString *)videoID {
liked = #"NULL";
NSString *clientID = #"myClientID";
NSString *clientSecret = #"myClientSecret";
self.auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:clientID
clientSecret:clientSecret];
NSString *urlStr = [NSString stringWithFormat:#"https://www.googleapis.com/youtube/v3/videos/getRating?id=%#&key=AIzaSyB437bMtpbJh-OrkieCDRtYLe6L1Ijb3Ww", videoID];
NSLog(#"URL FOR LIKE : %# auth:(%#)", urlStr, self.auth);
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSLog(#"stage 1");
[self.auth authorizeRequest:request
completionHandler:^(NSError *error) {
if (error == nil) {
// the request has been authorized
NSLog(#"ERROR LOADING AUTH 1 : %#", [error description]);
} else {
NSLog(#"ERROR LOADING AUTH 2 : %#", [error description]);
}
NSLog(#"stage 2");
NSString *output = nil;
if (error) {
output = [error description];
NSLog(#"ERROR FROM LOADING LIKE INFO : %#", output);
} else {
NSLog(#"stage 3");
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
[self displayAlertWithMessage:output];
if (data) {
// API fetch succeeded
NSLog(#"stage 32");
output = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
// NSLog(#"%#", data);
[self displayAlertWithMessage:output];
} else {
NSLog(#"stage 34");
// fetch failed
output = [error description];
[self displayAlertWithMessage:output];
}
}
NSData* json = [output dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *allCourses = [NSJSONSerialization
JSONObjectWithData:json
options:kNilOptions
error:&error];
NSArray *monday = allCourses[#"items"];
for ( NSDictionary *theCourse in monday )
{
liked = theCourse[#"rating"];
NSLog(#"LIKE INSIDE ARRAY : %#", theCourse[#"rating"]);
}
if( error )
{
NSLog(#"%#", [error localizedDescription]);
}
}];
NSLog(#"stage 4");
return liked;
}
The method runs because I can see it log the string, however it doesn't go through the authorizeRequest, it doesn't even print out the error messages. HOWEVER if my viewController is SampleRootViewController and I call the method from itself it works.
So basically
TestViewcontroller calls a method in SampleRootViewController -> doesn't go through authorizeRequest.
SampleRootViewController calls a method in SampleRootViewController (from itself) -> goes through authorizeRequest and works.
EDIT:
I found out what I was doing "wrong"
I was calling the method like this in background
[self performSelectorInBackground:#selector(getAuthDetails) withObject:nil];
instead of
[self getAuthDetails];

Related

EXC_BAD_ACCESS NSLog(#"Error decoding JSON data : %#", error.localizedDescription); on OpenWeather API

I am trying to use a location-based Weather app and it is crashing as soon as I open the view for that page.
This is the method;
- (WeatherModel *) parseWeatherData: (NSData *) data {
WeatherModel *weatherModel = [[WeatherModel alloc] init];
NSError *error;
id jsonObject = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingAllowFragments error: &error];
if (error) {
NSLog(#"Error decoding JSON data : %#", error.localizedDescription);
[self->_delegate didFailedWithError: error];
return nil;
}
if ([jsonObject isKindOfClass: [NSDictionary class]]) {
NSMutableDictionary *dicJsonObject = [[NSMutableDictionary alloc] initWithDictionary: jsonObject];
NSString *cityName = [dicJsonObject valueForKey: #"name"];
double temperature = [[[dicJsonObject valueForKey: #"main"] valueForKey: #"temp"] doubleValue];
NSMutableArray *arrWeatherData = [[NSMutableArray alloc] initWithArray: [dicJsonObject valueForKey: #"weather"]];
weatherModel.strCityName = cityName;
weatherModel.temperature = temperature;
weatherModel.weatherConditionID = [[[arrWeatherData objectAtIndex: 0] valueForKey: #"id"] intValue];
}
return weatherModel;
}
It is crashing on this line;
if (error) {
NSLog(#"Error decoding JSON data : %#", error.localizedDescription);
[self->_delegate didFailedWithError: error];
return nil;
}
If I comment out the NSLog section and [self->_delegate didFailedWithError: error];
the app does not crash, but then does not function as expected either...
The second part of the crash using debug is here;
- (void) fetchWeatherForCity: (NSString *) cityName {
_strWeatherURL = [NSString stringWithFormat: #"https://api.openweathermap.org/data/2.5/weather?q=%#&appid=bfea07812845ff9cb7e", cityName];
NSURL *weatherURL = [[NSURL alloc] initWithString: _strWeatherURL];
NSURLSessionConfiguration *urlSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration: urlSessionConfiguration];
NSURLSessionDataTask *task = [urlSession dataTaskWithURL: weatherURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(#"Error fetching weather data : %#", error);
[self->_delegate didFailedWithError: error];
return;
}
WeatherModel *weatherModel = [[WeatherModel alloc] init];
weatherModel = [self parseWeatherData: data];
if (weatherModel != nil) {
[self->_delegate didUpdateWeather: weatherModel];
}
}];
[task resume];
}
On this line;
weatherModel = [self parseWeatherData: data];

json NSJSONSerialization cannot load as the data is html

I get the following _downloadedData:
<!-- pageok -->
<!-- managed by puppet -->
<html>
<pre>pageok</pre>
</html>
When using the code listed below:
- (void)downloadItems
{
//Download the json file
NSURL *jsonFileUrl = [NSURL URLWithString:#"http://www.scratchclass.com:80/videos/"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:jsonFileUrl];
[request setHTTPMethod:#"GET"];
[request setValue:#"text/html" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"gzip" forHTTPHeaderField:#"Content-Encoding"];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
if(!connection){
NSLog(#"Connection Failed");
}
}
#pragma mark NSURLConnectionDataProtocol Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"didReceiveResponse: %#",response);
// Initialize the data object
_downloadedData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the newly downloaded data
[_downloadedData appendData:data];
NSString* newStr = [[NSString alloc] initWithData:_downloadedData encoding:NSUTF8StringEncoding ];
NSLog(#"didReceiveData: %#",newStr);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Create an array to store the videos
NSMutableArray *_videos = [[NSMutableArray alloc] init];
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
// Create a new video object and set its props to JsonElement properties
GetVideos1 *testVideo = [[GetVideos1 alloc] init];
testVideo.IDVideo = jsonElement[#"IDVideo"];
testVideo.title = jsonElement[#"title"];
// Add this question to the locations array
[_videos addObject:testVideo];
}
// Ready to notify delegate that data is ready and pass back items
if (self.delegate)
{
[self.delegate itemsDownloaded:_videos];
}
}
json is actually:
[{"IDVideo":"6Joup252fR0","title":"Top 30 Baseball
Plays"},{"IDVideo":"aAy3Sh_RXjc","title":"MLB top
plays"},{"IDVideo":"bkiaAGOoLjc","title":"Top 50 most unforgettable
ejections"}]
But I cannot get to the data as it is part of the #text node name. How can I fetch the data?
Here is the php code that was used:
<?php
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
// Constructor - open DB connection
function __construct() {
$this->db = new mysqli('idirectorySQL.db.9960960.hostedresource.com', 'idirectorySQL', 'iDirectory70!', 'idirectorySQL');
$this->db->autocommit(FALSE);
}
$con=mysqli_connect('idirectorySQL.db.9960960.hostedresource.com', 'idirectorySQL', 'iDirectory70!', 'idirectorySQL');
// This SQL statement selects ALL from the table 'videos'
$sql = "SELECT IDVideo,title FROM videos";
// Check if there are results
if ($result = mysqli_query($con, $sql)) {
// If so, then create a results array and a temporary one
// to hold the data
$resultArray = array();
$tempArray = array();
// Loop through each row in the result set
while($row = $result->fetch_object()) {
// Add each row into our results array
$tempArray = $row;
array_push($resultArray, $tempArray);
}
// Close connections
mysqli_close($con);
// Finally, encode the array to JSON and output the results
header('Content-type: application/json');
// echo json_encode($resultArray, JSON_PRETTY_PRINT);
echo json_encode($resultArray);
}
?>
I used
header('Content-type:application/x-www-form-urlencoded')
but I still received the same message. Also, this is what reads from the request headers:
Accept text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Don't set any custom parameters in the NSURLRequest, GET is the default anyway.
As NSURLConnection is deprecated, this is a solution with the recommended NSURLSession API
NSURL *url = [NSURL URLWithString:#"http://www.scratchclass.com:80/videos/"];
NSURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
if (error) {
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
} else {
NSError *jsonError;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
if (jsonError) {
NSLog(#"NSJSONSerialization failed! Error - %# %#",
[jsonError localizedDescription],
[[jsonError userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
} else {
NSMutableArray *videos = [[NSMutableArray alloc] init];
for (NSDictionary *item in jsonArray) {
GetVideos1 *testVideo = [[GetVideos1 alloc] init];
testVideo.IDVideo = item[#"IDVideo"];
testVideo.title = item[#"title"];
// Add this question to the locations array
[videos addObject:item];
}
if (self.delegate) {
[self.delegate itemsDownloaded:videos];
}
}
}
}] resume];

error 401 when i request to get all contacts using GDATA

i am using Gdata and already suceessfully login gmail and call below method
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth{
if (error != nil) {
}
else{
// i got successful login here
self.auth=auth;
}
}
in above method i got Authentifacation token and etc.
Now
NSString *urlStr = #"https://www.google.com/m8/feeds/contacts/default/full";
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[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] autorelease];
} else {
// fetch failed
output = [error description];
}
}
}];
but i got error 401
please helpful me

NSURLConnection wrong order

I have a NSURLConnection (two of them), and they're running in the wrong order.
Here's my method:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
}
}
else {
NSLog(#"error: %#", error);
}
}];
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
- (NSString *)percentEscapeString:(NSString *)string
{
NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
(CFStringRef)#" ",
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
return [result stringByReplacingOccurrencesOfString:#" " withString:#"+"];
}
So, it's two NSURLConnection's that are added to the [NSOperationQueue mainQueue]. What my output is showing me is that the second NSURLConnection is running before the first one. So it tries to go to the page where I download data before I'm logged in, so it (obviously) returns a "You're not logged in" error.
How do I schedule them one after another?
The issue, as I suspect you have realized, is that you're doing asynchronous network requests (which is good; you don't want to block the main queue), so there's no assurance of the order they'll finish.
The quickest and easiest answer is to simply put the call to the second request inside the completion block of the first one, not after it. You don't want to be making that second one unless the first one succeeded anyway.
To keep your code from getting unwieldy, separate the login from the request for main page. And you can use the completion block pattern which is common with asynchronous methods. You add a parameter to loginToMistarWithPin that specifies what it should do when the request finishes. You might have one completion block handler for success, and one for failure:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password success:(void (^)(void))successHandler failure:(void (^)(void))failureHandler {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
// assuming you validated that everything was successful, call the success block
if (successHandler)
successHandler();
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
if (failureHandler)
failureHandler();
}
}
else {
NSLog(#"error: %#", error);
if (failureHandler)
failureHandler();
}
}];
}
- (void)requestMainPage {
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[requestHome setHTTPMethod:#"GET"]; // this looks like GET request, not POST
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
Then, when you want to login, you can do something like:
[self loginToMistarWithPin:#"1234" password:#"pass" success:^{
[self requestMainPage];
} failure:^{
NSLog(#"login failed");
}];
Now, change those successHandler and failureHandler block parameters to include whatever data you need to pass back, but hopefully it illustrates the idea. Keep your methods short and tight, and use completion block parameters to specify what an asynchronous method should do when it's done.
Can you check the below link. It is about forcing one operation to wait for another.
NSOperation - Forcing an operation to wait others dynamically
Hope this helps.

Objective-c facebook graph api pagination

I'm developing an iOS app that includes a facebook feed of a users wall. Using the graph api with the following URL:
feedURL = [NSString stringWithFormat:#"https://graph.facebook.com/%#/feed?
access_token=%#&since=%#&until=%#",kFaceBookID,FBSession.activeSession.accessToken,
[dateRange objectForKey:#"since"], [dateRange objectForKey:#"until"]];
I get back data that only one result and a dictionary entry for paging. When I do a NSURLRequest with the "next" URL I get back 0 results. If I cut and paste that same URL into a web browser I get back 25 results. Any ideas on why?
Here is the code I am using:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *nextPageURL;
NSError *jsonError;
if (!jsonError) {
NSDictionary *rDict = [NSJSONSerialization JSONObjectWithData:_postData
options:0
error:&jsonError];
nextPageURL = [[rDict objectForKey:#"paging"]objectForKey:#"next"];
NSArray *rArray = [rDict objectForKey:#"data"];
DLog(#"Posts Dictionary = %#\n\n",rDict);
for (NSDictionary *rPost in rArray) {
FBPost *post = [[FBPost alloc]initFBPostWithDictionary:rPost];
[feedsArray addObject:post];
}
}
else
{
ALog(#"json error = %#",[jsonError localizedDescription]);
[activity stopAnimating];
NSString *errorMessage = [NSString stringWithFormat:#"Facebook status request failed with error: %#\nCheck your network connection and try again later",[jsonError localizedDescription]];
[self quit:errorMessage];
}
[feedsTable reloadData];
if (nextPageURL && [feedsArray count] < 30) {
DLog(#"Next Feed URL = %#",nextPageURL);
NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:nextPageURL]];
if (![[NSURLConnection alloc] initWithRequest:request delegate:self]) {
ALog(#"Connection failed for request: %#",request);
}
}
}
I am answering my own question as I took another look at the entire logic and completely changed my approach to use [FBRequestConnection...] instead. Here is the code if anyone is interested. Note that I fetch one weeks worth of feed messages at a time to improve the tableview performance.
- (void) fetchFBFeedsForDateRange:(NSDictionary *)dateRange;
{
_postData = [[NSMutableData alloc]init];
//since, until is a decremented one week at a time date range.
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
[dateRange objectForKey:#"since"], #"since",
[dateRange objectForKey:#"until"], #"until",
nil];
NSString *gPath = [NSString stringWithFormat:#"%#/feed",kFaceBookID];
[FBRequestConnection startWithGraphPath:gPath
parameters:params
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSArray *rArray = [result objectForKey:#"data"];
//DLog(#"Posts Array = %#\n\n",rArray);
for (NSDictionary *rPost in rArray) {
FBPost *post = [[FBPost alloc]initFBPostWithDictionary:rPost];
if (post.type) {
if (!post.skip) {
[feedsArray addObject:post];
}
}
}
[feedsTable reloadData];
if ([feedsArray count] < kFaceBookMaxPostsToDisplay) {
[self performSelector:#selector(fetchPreviousWeek)];
}
else
{
[activity stopAnimating];
}
}
else
{
[activity stopAnimating];
NSString *errorMessage = [NSString stringWithFormat:#"Facebook status request failed with error: %#\nCheck your network connection and try again later",[error localizedDescription]];
[self quit:errorMessage];
}
}];
}

Resources