Authentication login issue RESTful iOS - ios

Hey Folks i have a problem with authentication for the login credentials of my app. I currently have a rails web service running from my localhost, that features a main login screen for a admin user with password credentials to display functions.
The problem is that i am creating a RESTful iOS application running alongside my rails app, that just takes these JSON requests and bypasses them.
All i want is to create a way for me to enter admin and password without having to use auth_token? it seems to be the only way to do it, to simply authenticate the admin user first in the keychain. Im using the framework AFNetowrking for authentication.SSKeychain a wrapper for accounts, and SVProgressHUD for lightweight huds
.I also have the JSON and XML requests being logged in the i terminal but failing due to not being able to connect to the server with this error
error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo=0x7556d50 {NSErrorFailingURLStringKey=http://localhost:3000.json/, NSErrorFailingURLKey=http://localhost:3000.json/, NSLocalizedDescription=Could not connect to the server., NSUnderlyingError=0xeb805c0 "Could not connect to the server."}
this is how im storing the credentials for the authClient, all want to do is specify the same info used to log-into the web service client which is.
username:admin and password:taliesin
but unsure how to do this?
these are what i have so for the AuthAPIClient, CredentialsStore and LoginViewController
update if anybody knows a easier way to do this please can you let me know, will be much appreciated.
AuthAPIClient.m
#import "AuthAPIClient.h"
#import "CredentialStore.h"
#define BASE_URL #"http://admin:taliesin#localhost:3000"
#implementation AuthAPIClient
+ (id)sharedClient {
static AuthAPIClient *__instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *baseUrl = [NSURL URLWithString:BASE_URL];
__instance = [[AuthAPIClient alloc] initWithBaseURL:baseUrl];
});
return __instance;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (self) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setAuthTokenHeader];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tokenChanged:)
name:#"token-changed"
object:nil];
}
return self;
}
- (void)setAuthTokenHeader {
CredentialStore *store = [[CredentialStore alloc] init];
NSString *authToken = [store authToken];
[self setDefaultHeader:#"auth_token" value:authToken];
}
- (void)tokenChanged:(NSNotification *)notification {
[self setAuthTokenHeader];
}
#end
CredentialStore.m
#import "CredentialStore.h"
#import "SSKeychain.h"
#define SERVICE_NAME #"http://admin:taliesin#localhost:3000"
#define AUTH_TOKEN_KEY #"auth_token"
#implementation CredentialStore
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSString *user = [NSString stringWithFormat:#"%c%s%#", 'a', "a", #"a"];
NSString *password = [NSString stringWithFormat:#"%c%s%#", 'a', "a", #"a"];
NSURLCredential *credential = [NSURLCredential credentialWithUser:user
password:password
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
- (BOOL)isLoggedIn {
return [self authToken] != nil;
}
- (void)clearSavedCredentials {
[self setAuthToken:nil];
}
- (NSString *)authToken {
return [self secureValueForKey:AUTH_TOKEN_KEY];
}
- (void)setAuthToken:(NSString *)authToken {
[self setSecureValue:authToken forKey:AUTH_TOKEN_KEY];
[[NSNotificationCenter defaultCenter] postNotificationName:#"token-changed" object:self];
}
- (void)setSecureValue:(NSString *)value forKey:(NSString *)key {
if (value) {
[SSKeychain setPassword:#"taliesin"
forService:SERVICE_NAME
account:key];
} else {
[SSKeychain deletePasswordForService:SERVICE_NAME account:key];
}
}
- (NSString *)secureValueForKey:(NSString *)key {
return [SSKeychain passwordForService:SERVICE_NAME account:key];
}
#end
LoginViewApi.m
#import "LoginViewController.h"
#import "AuthAPIClient.h"
#import "CredentialStore.h"
#import "SVProgressHUD.h"
#interface UIViewController ()
#property (nonatomic, strong) IBOutlet UITextField *userTextField;
#property (nonatomic, strong) IBOutlet UITextField *passwordTextField;
#property (nonatomic, strong) CredentialStore *credentialStore;
#end
#implementation LoginViewController
+ (void)presentModallyFromViewController:(UIViewController *)viewController {
LoginViewController *loginViewController = [[LoginViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:loginViewController];
[viewController presentViewController:navController
animated:YES
completion:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.credentialStore = [[CredentialStore alloc] init];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(cancel:)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Login"
style:UIBarButtonItemStyleDone
target:self
action:#selector(login:)];
[self.userTextField becomeFirstResponder];
}
- (void)login:(id)sender {
[SVProgressHUD show];
id params = #{
#"admin": self.userTextField.text,
#"taliesin": self.passwordTextField.text
};
[[AuthAPIClient sharedClient] postPath:#"/auth/login.json"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *authToken = [responseObject objectForKey:#"auth_token"];
[self.credentialStore setAuthToken:authToken];
[SVProgressHUD dismiss];
[self dismissViewControllerAnimated:YES completion:nil];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == 500) {
[SVProgressHUD showErrorWithStatus:#"Something went wrong!"];
} else {
NSData *jsonData = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:nil];
NSString *errorMessage = [json objectForKey:#"error"];
[SVProgressHUD showErrorWithStatus:errorMessage];
}
}];
}
- (void)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Any help or for more questions please let me know cheers :)

Endpoint Addressing
I don't know how AFNetworking handles remote addresses but I think you are making a simple mistake when calling localhost from iOS to contact your web-service. The thing is that iOS is an operating system and it has its own localhost. Thus, when you think you are calling a remote web-service you are actually asking iOS to look within itself for this service. Therefore, wether you are running your rails web-service locally or on an actual remote server, find out the IP address for the server where the service is running and use that as an endpoint rather than localhost.
So update everything that points to your service to use an IP. Don't change it for 127.0.0.1, that would have the same effect.
For example:
#define BASE_URL #"http://admin:taliesin#192.168.0.1:3000"
Authentication: iOS
Assuming your web-service supports, that is, you have configured basic http authentication; then authenticating from iOS should not be a huge problem.
If you were to use RESTKit, it would be as simple as:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
objectManager.client.authenticationType = RKRequestAuthenticationTypeHTTPBasic;
objectManager.client.username = username;
objectManager.client.password = password;
I use Apple's KeychainItemWrapper for storing/retrieving credentials on iOS.
//Store Account on Keychain (disk) for persistence.
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
KeychainItemWrapper *accountItem = appDelegate.accountItem;
[accountItem setObject:password forKey:(__bridge id)kSecValueData];
[accountItem setObject:username forKey:(__bridge id)kSecAttrAccount];
username = password = nil; //keep account information only in the keychain.
I don't know if your code is correct but it should not be that complicated to handle basic http auth. Perhaps AFNetworking does not abstracts it as much as RESTKit does? Or perhaps you are not using the right methods from AFNetworking to do basic http auth? Basic auth does not require an access token and thus I suggest you read a little more about basic auth before implementing it.
Authentication: Rails
As for implementing authentication on Rails, it should not be too complicated either. All you need to do is to configure your controller to present an authentication challenge to the incoming request with basic http auth. One way to do this is by using before_filter: authenticate. Here is an example.
I think Railscasts is superb and they happen to have a tutorial on basic http auth and one specifically for rails 3.1. authentication.
Cheers.

Related

Why doesn’t iOS AWS Cognito login yield errors, yet doesn't execute its functions?

I'm trying to get my Cognito login to work.
The problem is that it's not working and I'm not getting error messages from AWS, or XCode. I've implemented it according to the tutorial and the AWS sample code (maybe wrongly?). I've tried to understand how the code works by adding a couple of NSlog's within the AWS cognito functions so that I know whether they get excecuted, but they do not show up in my console either. Why are these function not being run without even sending an error? Is there something obvious that I'm forgetting?
Here's the essential parts of my code
// loginviewcontroller.h
#import AWSCognitoIdentityProvider;
#interface LoginViewController : UIViewController <AWSCognitoIdentityPasswordAuthentication>
#property (nonatomic, strong) NSString * usernameText;
#end
loginviewcontroller.m file:
// loginviewcontroller.m
#property (nonatomic, strong) AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails*> *passwordAuthenticationCompletion;
- (IBAction)signInPressed:(UIButton *)sender {
self.passwordAuthenticationCompletion.result = [[AWSCognitoIdentityPasswordAuthenticationDetails alloc] initWithUsername:self.userName.text password:self.password.text];
NSLog(#"button pressed");};
-(void) getPasswordAuthenticationDetails: (AWSCognitoIdentityPasswordAuthenticationInput *) authenticationInput passwordAuthenticationCompletionSource: (AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails *> *) passwordAuthenticationCompletionSource {
//keep a handle to the completion, you'll need it continue once you get the inputs from the end user
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource;}
-(void) didCompletePasswordAuthenticationStepWithError:(NSError*) error {
NSLog(#"didCompletePasswordAuthenticationStepWithError");
dispatch_async(dispatch_get_main_queue(), ^{
//present error to end user
if(error){
NSLog(#"Error");
[[[UIAlertView alloc] initWithTitle:error.userInfo[#"__type"]
message:error.userInfo[#"message"]
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil] show];
}else{
NSLog(#"Success");
//dismiss view controller
[self dismissViewControllerAnimated:YES completion:nil];
}
});
appdelegate.h
//appdelegate.h
#import AWSCognitoIdentityProvider;
#interface AppDelegate : UIResponder <UIApplicationDelegate, AWSCognitoIdentityInteractiveAuthenticationDelegate>
#property(nonatomic,strong) LoginViewController* LoginViewController;
appdelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//setup AWS service config
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:nil];
//create a pool
AWSCognitoIdentityUserPoolConfiguration *configuration = [[AWSCognitoIdentityUserPoolConfiguration alloc] initWithClientId:#"xxxxxx" clientSecret:#"xxxxxxx" poolId:#"us-east-1_xxxxxx"];
[AWSCognitoIdentityUserPool registerCognitoIdentityUserPoolWithConfiguration:serviceConfiguration userPoolConfiguration:configuration forKey:#"us-east-1_xxxxx"];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"us-east-1_xxxxxx"];
pool.delegate = self;
return YES;
}
-(id<AWSCognitoIdentityPasswordAuthentication>) startPasswordAuthentication{
//implement code to instantiate and display login UI here
//return something that implements the AWSCognitoIdentityPasswordAuthentication protocol
NSLog(#"startpasswordauth AWS!");
return self.LoginViewController;
}
Also I did not understand this property line that's in the AWS github sample. The notation of *xxx I haven't see before. Here's the line:
#property (nonatomic, strong) AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails*> *passwordAuthenticationCompletion;
It's not mentioned in the tutorial, but without it
self.passwordAuthenticationCompletion.result = [[AWSCognitoIdentityPasswordAuthenticationDetails alloc] initWithUsername:self.userName.text password:self.password.text];
errors that the attribute is not found.
I have also tried this but delegate methods are not working.
Secondly, I tried with this code:
[AWSServiceManager.defaultServiceManager.defaultServiceConfiguration.credentialsProvider invalidateCachedTemporaryCredentials];
AWSCognitoIdentityUserPool *pool = [AWSCognitoIdentityUserPool CognitoIdentityUserPoolForKey:#"User"];
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
self.user = [delegate.pool currentUser];
[[ self.user getSession:_userName.text password:_txtPassword.text validationData:nil ] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserSession *> * _Nonnull task) {
//success, task.result has user session
dispatch_async(dispatch_get_main_queue(), ^{
if(task.error || task.isCancelled) {
[[[UIAlertView alloc] initWithTitle:task.error.userInfo[#"__type"]
message:task.error.userInfo[#"message"]
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil] show];
}else {
AWSCognitoIdentityUserSession *session = (AWSCognitoIdentityUserSession *) task.result;
NSString *tokenStr = [session.idToken tokenString];
[[NSUserDefaults standardUserDefaults]setObject:tokenStr forKey:#"token"];
[[NSUserDefaults standardUserDefaults]synchronize];
[self performSelectorOnMainThread:#selector(pushToDashbard) withObject:nil waitUntilDone:YES];
}});
return nil;
}]
If I am passing the right credentials then this is giving the token, with wrong credentials giving no error response.

Azure AD B2C getting oauthConnection Error: Bad Request

While trying to integrate Azure AD B2C, I am stuck with an error "oauthConnection Error: Bad Request". Following their given sample app it all works fine. But after integrating the same copy paste code from the working sample app, and trying to log in with Facebook or Google Plus, it throws an error! I am pretty sure that every credential that I used in the sample app is the same for my app. Any idea about this will be highly appreciated. Here is my code, AppDelegate.m
#import "AppData.h"
#import "NXOAuth2.h"
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupOAuth2AccountStore];
// Override point for customization after application launch.
return YES;
}
- (void)setupOAuth2AccountStore {
AppData *data = [AppData getInstance]; // The singleton we use to get the settings
NSDictionary *customHeaders =
[NSDictionary dictionaryWithObject:#"application/x-www-form-urlencoded"
forKey:#"Content-Type"];
// Azure B2C needs
// kNXOAuth2AccountStoreConfigurationAdditionalAuthenticationParameters for
// sending policy to the server,
// therefore we use -setConfiguration:forAccountType:
NSDictionary *B2cConfigDict = #{
kNXOAuth2AccountStoreConfigurationClientID : data.clientID,
kNXOAuth2AccountStoreConfigurationSecret : data.clientSecret,
kNXOAuth2AccountStoreConfigurationScope :
[NSSet setWithObjects:#"openid", data.clientID, nil],
kNXOAuth2AccountStoreConfigurationAuthorizeURL :
[NSURL URLWithString:data.authURL],
kNXOAuth2AccountStoreConfigurationTokenURL :
[NSURL URLWithString:data.tokenURL],
kNXOAuth2AccountStoreConfigurationRedirectURL :
[NSURL URLWithString:data.bhh],
kNXOAuth2AccountStoreConfigurationCustomHeaderFields : customHeaders,
// kNXOAuth2AccountStoreConfigurationAdditionalAuthenticationParameters:customAuthenticationParameters
};
[[NXOAuth2AccountStore sharedStore] setConfiguration:B2cConfigDict
forAccountType:data.accountIdentifier];
}
LoginViewController.m
#import "AppData.h"
#import "LoginViewController.h"
#import "NXOAuth2.h"
#interface LoginViewController ()
#end
#implementation LoginViewController {
NSURL *myLoadedUrl;
bool isRequestBusy;
}
// Put variables here
- (void)viewDidLoad {
[super viewDidLoad];
// OAuth2 Code
self.loginView.delegate = self;
[self requestOAuth2Access];
[self setupOAuth2AccountStore];
NSURLCache *URLCache =
[[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
}
- (void)resolveUsingUIWebView:(NSURL *)URL {
// We get the auth token from a redirect so we need to handle that in the
// webview.
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(resolveUsingUIWebView:)
withObject:URL
waitUntilDone:YES];
return;
}
NSURLRequest *hostnameURLRequest =
[NSURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0f];
isRequestBusy = YES;
[self.loginView loadRequest:hostnameURLRequest];
NSLog(#"resolveUsingUIWebView ready (status: UNKNOWN, URL: %#)",
self.loginView.request.URL);
}
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
AppData *data = [AppData getInstance];
NSLog(#"webView:shouldStartLoadWithRequest: %# (%li)", request.URL,
(long)navigationType);
// The webview is where all the communication happens. Slightly complicated.
myLoadedUrl = [webView.request mainDocumentURL];
NSLog(#"***Loaded url: %#", myLoadedUrl);
// if the UIWebView is showing our authorization URL or consent URL, show the
// UIWebView control
if ([request.URL.absoluteString rangeOfString:data.authURL
options:NSCaseInsensitiveSearch]
.location != NSNotFound) {
self.loginView.hidden = NO;
} else if ([request.URL.absoluteString rangeOfString:data.loginURL
options:NSCaseInsensitiveSearch]
.location != NSNotFound) {
// otherwise hide the UIWebView, we've left the authorization flow
self.loginView.hidden = NO;
} else if ([request.URL.absoluteString rangeOfString:data.bhh
options:NSCaseInsensitiveSearch]
.location != NSNotFound) {
// otherwise hide the UIWebView, we've left the authorization flow
self.loginView.hidden = YES;
[[NXOAuth2AccountStore sharedStore] handleRedirectURL:request.URL];
} else {
self.loginView.hidden = NO;
}
return YES;
}
#pragma mark - UIWebViewDelegate methods
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// The webview is where all the communication happens. Slightly complicated.
}
- (void)handleOAuth2AccessResult:(NSURL *)accessResult {
// parse the response for success or failure
if (accessResult)
// if success, complete the OAuth2 flow by handling the redirect URL and
// obtaining a token
{
[[NXOAuth2AccountStore sharedStore] handleRedirectURL:accessResult];
} else {
// start over
[self requestOAuth2Access];
}
}
- (void)setupOAuth2AccountStore {
[[NSNotificationCenter defaultCenter]
addObserverForName:NXOAuth2AccountStoreAccountsDidChangeNotification
object:[NXOAuth2AccountStore sharedStore]
queue:nil
usingBlock:^(NSNotification *aNotification) {
if (aNotification.userInfo) {
// account added, we have access
// we can now request protected data
NSLog(#"Success!! We have an access token.");
} else {
// account removed, we lost access
}
}];
[[NSNotificationCenter defaultCenter]
addObserverForName:NXOAuth2AccountStoreDidFailToRequestAccessNotification
object:[NXOAuth2AccountStore sharedStore]
queue:nil
usingBlock:^(NSNotification *aNotification) {
NSError *error = [aNotification.userInfo
objectForKey:NXOAuth2AccountStoreErrorKey];
// Always got stuck here while trying to login with any credentials
NSLog(#"Error!! %#", error.localizedDescription);
}];
}
- (void)requestOAuth2Access {
AppData *data = [AppData getInstance];
[[NXOAuth2AccountStore sharedStore]
requestAccessToAccountWithType:data.accountIdentifier
withPreparedAuthorizationURLHandler:^(NSURL *preparedURL) {
NSURLRequest *r = [NSURLRequest requestWithURL:preparedURL];
[self.loginView loadRequest:r];
}];
}
ViewController.m
#import "ViewController.h"
#import "AppData.h"
#import "LoginViewController.h"
#import "NXOAuth2.h"
// Login Action
- (IBAction)login:(id)sender {
LoginViewController *userSelectController =
[self.storyboard instantiateViewControllerWithIdentifier:#"login"];
[self.navigationController pushViewController:userSelectController
animated:YES];
}
In case if anybody stumbles in this, Here is the solution
Go to pod, NXOAuth2Client.m and replace the method
- (void)requestTokenWithAuthGrant:(NSString *)authGrant redirectURL:(NSURL *)redirectURL; with the below code
- (void)requestTokenWithAuthGrant:(NSString *)authGrant redirectURL:(NSURL *)redirectURL;
{
NSAssert1(!authConnection, #"authConnection already running with: %#", authConnection);
NSMutableURLRequest *tokenRequest = [NSMutableURLRequest requestWithURL:tokenURL];
[tokenRequest setHTTPMethod:self.tokenRequestHTTPMethod];
[authConnection cancel]; // just to be sure
self.authenticating = YES;
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"authorization_code", #"grant_type",
clientId, #"client_id",
// clientSecret, #"client_secret",
[redirectURL absoluteString], #"redirect_uri",
authGrant, #"code",
nil];
if (self.desiredScope) {
[parameters setObject:[[self.desiredScope allObjects] componentsJoinedByString:#" "] forKey:#"scope"];
}
if (self.customHeaderFields) {
[self.customHeaderFields enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[tokenRequest addValue:obj forHTTPHeaderField:key];
}];
}
if (self.additionalAuthenticationParameters) {
[parameters addEntriesFromDictionary:self.additionalAuthenticationParameters];
}
authConnection = [[NXOAuth2Connection alloc] initWithRequest:tokenRequest
requestParameters:parameters
oauthClient:self
delegate:self];
authConnection.context = NXOAuth2ClientConnectionContextTokenRequest;
}
Commenting clientSecret solved the issue

How to query the inbox content of GMail by GMail API in Swift

I need to integrate GMail inbox in my application after authentication.So how can I query the inbox content of GMail by using the API. And also I have to access the other features too.So Please help me to find the exact swift code to access the GMail.
Using Google Docs iOS Quickstart:
Step 1: Turn on the Gmail API
Step 2: Prepare the workspace
Step 3: Set up the sample
Here is a sample code, replace the contents of the ViewController.h file with the following code:
#import <UIKit/UIKit.h>
#import "GTMOAuth2ViewControllerTouch.h"
#import "GTLGmail.h"
#interface ViewController : UIViewController
#property (nonatomic, strong) GTLServiceGmail *service;
#property (nonatomic, strong) UITextView *output;
#end
Replace the contents of ViewController.m with the following code:
#import "ViewController.h"
static NSString *const kKeychainItemName = #"Gmail API";
static NSString *const kClientID = #"YOUR_CLIENT_ID_HERE";
#implementation ViewController
#synthesize service = _service;
#synthesize output = _output;
// When the view loads, create necessary subviews, and initialize the Gmail API service.
- (void)viewDidLoad {
[super viewDidLoad];
// Create a UITextView to display output.
self.output = [[UITextView alloc] initWithFrame:self.view.bounds];
self.output.editable = false;
self.output.contentInset = UIEdgeInsetsMake(20.0, 0.0, 20.0, 0.0);
self.output.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.output];
// Initialize the Gmail API service & load existing credentials from the keychain if available.
self.service = [[GTLServiceGmail alloc] init];
self.service.authorizer =
[GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientID
clientSecret:nil];
}
// When the view appears, ensure that the Gmail API service is authorized, and perform API calls.
- (void)viewDidAppear:(BOOL)animated {
if (!self.service.authorizer.canAuthorize) {
// Not yet authorized, request authorization by pushing the login UI onto the UI stack.
[self presentViewController:[self createAuthController] animated:YES completion:nil];
} else {
[self fetchLabels];
}
}
// Construct a query and get a list of labels from the user's gmail. Display the
// label name in the UITextView
- (void)fetchLabels {
self.output.text = #"Getting labels...";
GTLQueryGmail *query = [GTLQueryGmail queryForUsersLabelsList];
[self.service executeQuery:query
delegate:self
didFinishSelector:#selector(displayResultWithTicket:finishedWithObject:error:)];
}
- (void)displayResultWithTicket:(GTLServiceTicket *)ticket
finishedWithObject:(GTLGmailListLabelsResponse *)labelsResponse
error:(NSError *)error {
if (error == nil) {
NSMutableString *labelString = [[NSMutableString alloc] init];
if (labelsResponse.labels.count > 0) {
[labelString appendString:#"Labels:\n"];
for (GTLGmailLabel *label in labelsResponse.labels) {
[labelString appendFormat:#"%#\n", label.name];
}
} else {
[labelString appendString:#"No labels found."];
}
self.output.text = labelString;
} else {
[self showAlert:#"Error" message:error.localizedDescription];
}
}
// Creates the auth controller for authorizing access to Gmail API.
- (GTMOAuth2ViewControllerTouch *)createAuthController {
GTMOAuth2ViewControllerTouch *authController;
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
NSArray *scopes = [NSArray arrayWithObjects:kGTLAuthScopeGmailReadonly, nil];
authController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:[scopes componentsJoinedByString:#" "]
clientID:kClientID
clientSecret:nil
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
return authController;
}
// Handle completion of the authorization process, and update the Gmail API
// with the new credentials.
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)authResult
error:(NSError *)error {
if (error != nil) {
[self showAlert:#"Authentication Error" message:error.localizedDescription];
self.service.authorizer = nil;
}
else {
self.service.authorizer = authResult;
[self dismissViewControllerAnimated:YES completion:nil];
}
}
// Helper for showing an alert
- (void)showAlert:(NSString *)title message:(NSString *)message {
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
#end
Step 4: Run the sample
Notes: Authorization information is stored in your Keychain, so subsequent executions will not prompt for authorization.
You can review and learn more about iOS and Google API(GMAIL API) in https://developers.google.com/gmail/api/v1/reference/ to apply other feature you want to add.
I hope this helps :)

iOS WebService doesn't seem to be starting. Anyway I can find out if its doing anything?

I have built a WebService to retrieve user specific reports using a PHP API. I am fairly new to this so I followed some tutorials and help pages to build the web service. It doesn't seem to run at all so I am thinking I stuffed up or just incorrectly put code in that didn't belong as I was following multiple tutorials etc to get the desired result.
here is the code for .m file of the web service:
#import "reportsTestViewController.h"
#import "ReportsDataObject.h"
#interface reportsTestViewController ()
#end
#implementation reportsTestViewController
#synthesize label;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.tesg.com.au/allCustBuild.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
//connect
label.text = #"connecting...";
} else {
//error
}
}
-(void)setupReportsFromJSONArray:(NSData*)dataFromReportsArray{
NSError *error;
NSMutableArray *reportsArray = [[NSMutableArray alloc] init];
NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromReportsArray options:0 error:&error];
if(error){
NSLog(#"error parsing the json data from server with error description - %#", [error localizedDescription]);
}
else {
reportsArray = [[NSMutableArray alloc] init];
for(NSDictionary *eachReport in arrayFromServer)
{
ReportsDataObject *report = [[ReportsDataObject alloc] initWithJSONData:eachReport];
[reportsArray addObject:report];
}
//Now you have your reportsArray filled up with all your data objects
}
}
-(void)connectionWasASuccess:(NSData *)data{
[self setupReportsFromJSONArray:data];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
//We check against table to make sure we are displaying the right number of cells
// for the appropriate table. This is so that things will work even if one day you
//decide that you want to have two tables instead of one.
if(tableView == reportsTable){
return([theReportsArray count]);
}
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell)
{
//set your configuration of your cell
}
//The beauty of this is that you have all your data in one object and grab WHATEVER you like
//This way in the future you can add another field without doing much.
if([theReportsArray count] == 0){
cell.textLabel.text = #"no reports to show";
}
else{
ReportsDataObject *currentReport = [theReportsArray objectAtIndex:indexPath.row];
cell.textLabel.text = [currentReport buildingName];
// in the future you can grab whatever data you need like this
//[currentPlace placeName], or [currentPlace placeDay];
}
return(cell);
}
#end
and the code for the .h:
#import <UIKit/UIKit.h>
#interface reportsTestViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
IBOutlet UITableView *reportsTable;
IBOutlet UILabel *Label;
NSArray *theReportsArray;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#end
I'm sure it's something really ignorant that i've done but after going back through help pages and tuts, i can't find what I did wrong.
It looks like you are establishing your connection ok, but you are never handling any data that gets sent back. Once you create a connection you need to add the delegate methods to handle the data sent back by the connection.
As mentioned in some of the comments you also need to verify that your php page on the server is indeed giving you the information you expect. By logging out the data string as below in the -connectionDidFinishLoading you will be able to see any data sent back from the server for debugging.
//create an NSMutableData property in your interface
#property (nonatomic, strong) NSMutableData *myDataIvar;
//initialize it when you create your connection
if (connection){
self.myDataIvar = [[NSMutableData alloc] init];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[self.myDataIvar setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.myDataIvar appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Connection Failed: %#", error.userInfo);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//this is where you would parse the data received back from the server
NSString *responseString = [[NSString alloc] initWithData:self.myDataIvar encoding:NSUTF8StringEncoding];
NSLog(#"Received Data: %#",responseString);
[self setupReportsFromJSONArray:self.myDataIvar];
}
This way at least you will be able to see what data you are receiving back from the server.
EDIT:
Also your test page does not seem to be putting out any json data. When I navigate to it all I get is "how".
I put up a quick php page that you can use to test your obj-c code. It will echo back an array with 10 test results to fill your tableview if your code is correct.
//Create your request pointing to the test page
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.codeyouniversity.com/json_test.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
If you want to put the php on your own page for testing this is what I used.
<?php
$jsonArray = array('result1','result2','result3','result4','result5','result6','result7','result8','result9','result10');
echo json_encode($jsonArray);
?>
Here's a link to Apples documentation for the URL loading system as well
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i
try this one if you get result, your url is wrong.I think now itself your url is wrong
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://api.openweathermap.org/data/2.5/weather?q=London,uk"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
//connect
label.text = #"connecting...";
} else {
//error
}
}

Override UIWebView user agent - but not for Facebook SDK

I'm setting a custom UserAgent for the webviews in my app as per the instructions in this other question. Specifically, I'm setting
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"MyApp/MyLongVersionInfoString", #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
at app launch. (Just setting the appropriate headers for the NSMutableURLRequest to be used by the UIWebView - UserAgent, User-Agent, User_Agent - does not work.)
This causes my embedded webviews to use the correct user agent. However, it also breaks the embedded webviews used by the Facebook SDK for dialogs - after I post to my wall, for instance, the contents of the FB dialog webview are replaced with text similar to window.location.href="fbconnect:\/\/success?post_id=100002469633196_43677789308119... and the webview does not close as it normally would (the user has to manually close it). This only occurs when I have my custom user agent set.
I thought I could circumvent the problem by unsetting the user agent before Facebook calls and resetting it afterwards, but it seems I can't unset the default defaults; I tried calling [[NSUserDefaults standardUserDefaults] removeObjectForKey:#"UserAgent"] and [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:NSRegistrationDomain] before each Facebook call and setting them again in the call's result handler, but I still see the same misbehavior.
I tried switching my initial settings to [[NSUserDefaults standardUserDefaults] setObject:newUA forKey:#"UserAgent"];, but then my webviews don't pick up the user agent.
Surely, surely, someone has used the Facebook SDK before in an app with non-Facebook embedded webviews. What am I missing? I've gone a number of rounds on this, each of which seems to almost-but-not-quite fix everything.
I ended up having to do this by implementing an NSURLProtocol subclass. While my implementation was messy, I'm including a scrubbed version below because I was surprised not to be able to find an example already on StackOverflow.
#import <Foundation/Foundation.h>
#interface MyProtocol : NSURLProtocol {
NSURLConnection *connection;
}
#property (nonatomic, retain) NSURLConnection *connection;
#end
#implementation MyProtocol
#synthesize connection;
#pragma mark -
#pragma mark custom methods
+ (NSString *)myUA {
return [[NSUserDefaults standardUserDefaults] stringForKey:#"MyCustomUserAgent"];
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSString *scheme = [request.URL.scheme stringByAppendingString:#"://"];
NSString *baseRequestString = [[[request.URL absoluteString] stringByReplacingOccurrencesOfString:request.URL.path withString:#""]
stringByReplacingOccurrencesOfString:scheme withString:#""];
if (request.URL.query != nil) {
NSString *query = [#"?" stringByAppendingString:request.URL.query];
baseRequestString = [baseRequestString stringByReplacingOccurrencesOfString:query withString:#""];
}
BOOL shouldIntercept = [baseRequestString isEqualToString:myWebHost];
BOOL alreadyIntercepted = [[NSURLProtocol propertyForKey:#"UserAgent" inRequest:request] isEqualToString:[self myUA]];
return shouldIntercept && !alreadyIntercepted;
}
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:request.URL
cachePolicy:request.cachePolicy
timeoutInterval:request.timeoutInterval];
[mutableRequest setAllHTTPHeaderFields:[request allHTTPHeaderFields]];
[mutableRequest setHTTPMethod:[request HTTPMethod]];
if ([request HTTPBody] != nil)
[mutableRequest setHTTPBody:[request HTTPBody]];
if ([request HTTPBodyStream] != nil)
[mutableRequest setHTTPBodyStream:[request HTTPBodyStream]];
[NSURLProtocol setProperty:[[self class] myUA] forKey:#"UserAgent" inRequest:mutableRequest];
[mutableRequest setValue:[[self class] myUA] forHTTPHeaderField:#"User-Agent"];
[mutableRequest setValue:[[self class] myUA] forHTTPHeaderField:#"User_Agent"];
self = [super initWithRequest:mutableRequest cachedResponse:cachedResponse client:client];
return self;
}
- (void) dealloc
{
[connection release];
}
#pragma mark -
#pragma mark boilerplate NSURLProtocol subclass requirements
- (void) startLoading
{
self.connection = [[NSURLConnection connectionWithRequest:[self request] delegate:self] retain];
}
- (void)stopLoading
{
[self.connection cancel];
}
- (void)connection:(NSURLConnection*)conn didReceiveResponse:(NSURLResponse*)response
{
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:[[self request] cachePolicy]];
}
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
[[self client] URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)conn
{
[[self client] URLProtocolDidFinishLoading:self];
}
- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)theRequest redirectResponse:(NSURLResponse*)redirectResponse
{
return theRequest;
}
- (void)connection:(NSURLConnection*)conn didFailWithError:(NSError*)error
{
[[self client] URLProtocol:self didFailWithError:error];
}
#end
Note that the protocol must be registered with [NSURLProtocol registerClass:[MyProtocol class]]; before you make any web requests to which you want it to apply - for instance, in applicationDidFinishLaunchingWithOptions:.

Resources