James Qin James Qin - 23 days ago 7
Objective-C Question

ios - How to obtain the authorization code using safari to login and exchange access token?

I have recently build an app, but the last step is to post the data to Runkeeper website. My app have the view controller, it has the button to login and deauthorize. But when I using Oauth2 by open the safari to login the account for my app. When come back from the safari and reopen my app. I don't know how to check if I have got the authorization code. And also I want to exchange the access token to post my data to the Runkeeper website. But I am totally new to connect the app with API. I have see a lot of tutorial about the google API , youtube API, but the they are different with Runkeeper. This is my code:

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"View did Load!");
kIDMOAuth2ClientId = @"....";
kIDMOAuth2ClientSecret = @"....";
kIDMOAuth2AuthorizationURL = @"https://runkeeper.com/apps/authorize";
kIDMOAuth2TokenURL = @"https://runkeeper.com/apps/token";
kIDMOAuth2AccountType = @"token";//authorization_code //Runkeeper API
kIDMOAuth2SuccessPagePrefix = @"Success";
kIDMOAuth2RedirectURL = @"Landice://";
kIDMOAuth2DeauthorizationURL = @"https://runkeeper.com/apps/de-authorize";
runkeeperButton *runkeeperString=[runkeeperButton getInstance];
NSLog(@"%@", runkeeperString.str);
if ((runkeeperString.str == NULL) || [runkeeperString.str isEqualToString:@"Authorize"]) {
buttonToggled = YES;
[loginandoutButton setTitle:@"Login & Authorize" forState:UIControlStateNormal];
NSLog(@"%@", runkeeperString.str);
}else if ([runkeeperString.str isEqual:@"Deauthorize"]){
buttonToggled = NO;
[loginandoutButton setTitle:@"Deauthorize" forState:UIControlStateNormal];
NSLog(@"%@", runkeeperString.str);
loginandoutButton.layer.cornerRadius = 10;

- (IBAction)toggleButton:(id)sender {
runkeeperButton *runkeeperString=[runkeeperButton getInstance];
// "Deauthorize" button clicked
if (!buttonToggled) {
[sender setTitle:@"Login & Authorize" forState:UIControlStateNormal];
buttonToggled = YES;
runkeeperString.str = @"Authorize";
// "Login & Authorize" button clicked
else {
[sender setTitle:@"Deauthorize" forState:UIControlStateNormal];
buttonToggled = NO;
runkeeperString.str = @"Deauthorize";
//[self requestOAuth2Access];

NSURL *url = [NSURL URLWithString:@"https://runkeeper.com/apps/authorize?response_type=code&client_id=[SOMETHING]&redirect_uri=landice://response&duration=permanent&scope=read"];
[[UIApplication sharedApplication] openURL:url];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];

else if (![[UIApplication sharedApplication] canOpenURL:url]) {
NSLog(@"%@%@",@"Failed to open url:",[url description]);

My question is:
1. how to get the authorization code from safari and how to check if I have got it.
2. I heard that authorization code is not stable, so how to exchange the access token and how to check the access token is expired. If expired, how to get another one.
3. how to post data from core data by JSON to the Runkeeper website.

Thanks in Advance.


Do you have to open the site in Safari? I'm not sure it is even possible to get data back from another app like this, especially if it's not in an app group you define yourself (and you can only put your own apps into such a group). Basically Safari is sandboxed, just like your app. I don't see how the data that is loaded from a page can be given back to your app.

Instead, I would advice you to design your own small browser window in your app for this purpose, using WKWebView. Then you can skip the [[UIApplication sharedApplication] openURL:url] and instead open another view that loads the Runkeeper page. From there you should be able to access the data by evaluating java script (or inject your own script for this). I don't know Runkeeper myself, so I don't know how exactly you would do that, but I'm sure there is a way somehow.

This is more work, but would have the benefit that your users don't have to switch apps for the authorization.

Oh, and all this assumes you definitely have to load a page and can't use some API that Runkeeper provides. Maybe you could also just use an NSURLSessionDataTask to call the authorization and simply get the token from the NSHTTPURLResponse object on its return?