iPhone CHDropboxSync crash to home screen after linking account - iphone

I'm very new to using dropbox, and am looking for examples of syncing. I found CHDropboxSync and am in process of making the sample app included with the project work.
I'm running into the issue where dropbox "link with dropbox" webpage opens with prompt "ExampleApp wants access to a folder in your dropbox". The folder name appears correct. When I tap the green "Allow" button, the web browser closes and a black screen appears. A second later I see the iPhone's desktop. There are no error messages, but the app does not reopen after linking a dropbox account. What could be causing this issue? Has anyone else encountered such "crash to desktop" kind of issue with CHDropboxSync?
Here are the modifications that I made to the project to make it work:
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>db-CORRECT_APP_KEY</string>
</array>
</dict>
In the app delegate:
// Dropbox
#warning Put your app-folder-type dropbox keys in here
DBSession* dbSession = [[[DBSession alloc] initWithAppKey:#"CORRECT_KEY" appSecret:#"CORRECT_SECRET" root:kDBRootAppFolder] autorelease];
[DBSession setSharedSession:dbSession];
Thank you for any help!

I solved the problem by:
Creating a different dropbox app and updating the project with new key/secret/root folder
Implementing a different app delegate method:
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
NSLog(#"App linked successfully!");
[CHDropboxSync forgetStatus];
[[NSNotificationCenter defaultCenter] postNotificationName:#"Linked" object:nil];
// At this point you can start making API calls
}
return YES;
}
// Add whatever other url handling code your app requires here
return NO;
}

Related

open an iphone app from a link in a web app in safari

I am trying to open my native iPhone application from a link in safari. I have followed this link to create a url schema. I have added appgo:// as my url schema and com.xxxx.appgo as Identifier under URL Types. Following is my link in web page in safari: Open iPhone App
But when I click on the link the app doesn't open up and safari produce an alert with error: Safari cannot enter the page as the address is invalid.
my bundle identifier: com.xxxxx.abc
Note: My bundle identifier is different from the identifier in URL types. Can that be an issue?
Edit:
I have made bundle identifier and url identifier same. I have also added following code in my app delegate:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
NSLog(#"application");
BOOL returnValue = NO;
NSString *urlString = [url absoluteString];
if([urlString hasPrefix:#"com.xxxx.appgo"]){
returnValue = YES;
}
return returnValue;
}
I am testing it in my iPad. I first install the latest version of app from xcode and then press on home button and then open the link in safari. But I am still getting the alert saying the Safari cannot enter the page as address is invalid. My link in safari:
Open iPhone App
Implement in app delegate
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
BOOL returnValue = NO;
NSString *urlString = [url absoluteString];
if([urlString hasPrefix:#"appgo://"]){
returnValue = YES;
}
return returnValue;
}
Edit:
Add in your info.plist file
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.xxxx.appgo</string>
<key>CFBundleURLSchemes</key>
<array>
<string>appgo</string>
<string>yourSecondScheme</string>
</array>
</dict>
</array>
Yes. Your bundle identifier must be same as the URL types in the plis file.
For more you can refer to this link.

Google + iPhone API sign in and share without leaving app

I recently integrated the Google + API in my App, it was a breeze, my only problem with it, is that everything requires you to leave the app and then come back (it uses URL schemes for this). This is not the behavior I would like, is there a way to directly call their services and do whatever I want with the responses just like in LinkedIn API?.
I really want to avoid going back and forth between safari and my app. Any suggestions/documentation is appreciated.
Thank you,
Oscar
UPDATE FROM GOOGLE
today, we released a new Google Sign In iOS SDK with full built-in
support for Sign In via WebView:
developers.google.com/identity/sign-in/ios The SDK supports dispatch
to any of a number of Google apps handling Sign In when present, with
WebView fallback after. In all cases, the Safari switch is avoided,
which we've seen to be the key element in avoiding app rejection.
We're looking forward to getting feedback from people using the new
SDK, and hope its use can replace the (ingenious and diligent)
workarounds people have implemented in the meantime.
THE METHOD BELLOW IS NO LONGER NEEDED
THIS METHOD HANDLES THE LOGIN INTERNAL WITH A CUSTOM UIWebView
THIS WORKS AND WAS APPROVED BY APPLE
My app got kicked from review cause of this
"The app opens a web page in mobile Safari for logging in to Google plus,
then returns the user to the app. The user should be able log in without opening
Safari first."
See this link https://code.google.com/p/google-plus-platform/issues/detail?id=900
I did solved it by following steps
1) create a subclass of UIApplication, which overrides openURL:
.h
#import <UIKit/UIKit.h>
#define ApplicationOpenGoogleAuthNotification #"ApplicationOpenGoogleAuthNotification"
#interface Application : UIApplication
#end
.m
#import "Application.h"
#implementation Application
- (BOOL)openURL:(NSURL*)url {
if ([[url absoluteString] hasPrefix:#"googlechrome-x-callback:"]) {
return NO;
} else if ([[url absoluteString] hasPrefix:#"https://accounts.google.com/o/oauth2/auth"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationOpenGoogleAuthNotification object:url];
return NO;
}
return [super openURL:url];
}
#end
this will basically prevent anything to be opened from Chrome on iOS
we catch the auth call and redirect it to our internal UIWebView
2) to info.plist, add the Principal class, and for it Application (or whatever you named the class)
Add plist key "NSPrincipalClass" and as the value the class of your main application (class which extends UIApplication, in this case Application (see code above))
3) catch the notification and open an internal webview
When your custom Application class sends ApplicationOpenGoogleAuthNotification, listen for it somewhere (in the AppDelegate maybe) and when you catch this notification, open a UIWebView (use the URL passed by the notification as the url for the webview) (in my case the LoginViewController listens for this notification and when received, it opens a view controller containing only a webview hooked up to delegate)
4) inside the webview
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ([[[request URL] absoluteString] hasPrefix:#"com.XXX.XXX:/oauth2callback"]) {
[GPPURLHandler handleURL:url sourceApplication:#"com.google.chrome.ios"n annotation:nil];
// Looks like we did log in (onhand of the url), we are logged in, the Google APi handles the rest
[self.navigationController popViewControllerAnimated:YES];
return NO;
}
return YES;
}
or simmilar code, that handles the response
com.XXX.XXX:/oauth2callback from code above, replace with your company and app identifier, like "com.company.appname:/oauth2callback"
you might want to use #"com.apple.mobilesafari" as sourceApplication parameter
So, it depends what you want to do.
Sign-In: this will always call out to another application. If the Google+ application is installed it will call out to that, else it will fall back to Chrome and Safari.
Sharing/Interactive Posts: right now this always uses Chrome or Mobile Safari.
Retrieving friends, writing app activities, retrieving profile information: All this is done with the access token retrieved after sign in, so does not require leaving the application.
It is possible, though rather unsupported, to skip the SDK and pop up a UIWebView, construct the OAuth link dynamically and send the user to that (take a look at GTMOAuth2ViewControllerTouch in the open source libraries that ship with the SDK). Below is the a very rough example of the kind of thing you could do to plumb it back into the GPPSignIn instance.
However, you would be guaranteeing that the user has to enter their username and password (and maybe 2nd factor). With the Google+ app you're pretty much guaranteed to be already signed in, and with the Chrome/Safari route, there is a chance the user is already signed in (particularly if they're using other apps with Google+ Sign-In).
This also doesn't address sharing, so I would strongly recommend using the existing SDK as far as possible. Filing a feature request for the way you would prefer it to work would be a good thing to do as well: https://code.google.com/p/google-plus-platform/issues/list
#interface ViewController() {
GTMOAuth2ViewControllerTouch *controller;
}
#end;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GPPSignIn *signIn = [GPPSignIn sharedInstance];
signIn.clientID = #""; // YOUR CLIENT ID HERE.
signIn.delegate = self;
}
- (IBAction)didTapSignIn:(id)sender {
void (^handler)(id, id, id) =
^(GTMOAuth2ViewControllerTouch *viewController,
GTMOAuth2Authentication *auth,
NSError *error) {
[self dismissViewControllerAnimated:YES completion:^{
[controller release];
}];
if (error) {
NSLog(#"%#", error);
return;
} else {
BOOL signedIn = [[GPPSignIn sharedInstance] trySilentAuthentication];
if(!signedIn) {
NSLog(#"Sign In failed");
}
}
};
controller = [[GTMOAuth2ViewControllerTouch
controllerWithScope:kGTLAuthScopePlusLogin
clientID:[GPPSignIn sharedInstance].clientID
clientSecret:nil
keychainItemName:[GPPSignIn sharedInstance].keychainName
completionHandler:handler] retain];
[self presentViewController:controller animated:YES completion:nil];
}
- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (!error) {
UIAlertView * al = [[UIAlertView alloc] initWithTitle:#"Authorised"
message:#"Authorised!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[al show];
[al release];
}
}
The only real trick to this code is that it uses the [GPPSignIn sharedInstance].keychainName - this means that the auth tokens get stored in the same keychain entry as the GPPSignIn button would, which in turn means we can use [[GPPSignIn sharedInstance] trySilentAuthentication] once it has been populated, and keep the same callback based flow as the main library.
#PeterLapisu approach works good if the Google Plus App is not installed.
Then outgoing url prefixes from app are as follows:
#"googlechrome-x-callback:"
#"https://accounts.google.com/o/oauth2/auth"
However if the Google App is installed there is one more outgoing url and the prefix list looks as follows:
#"com.google.gppconsent.2.4.1:"
#"googlechrome-x-callback:"
#"https://accounts.google.com/o/oauth2/auth"
So, if the Google App is installed, it will be launched simultaneously with our app UIViewController that contains webview.Then if user sucessfully logs in with Google App he will be directed back to our app and the ViewController will be visible.
To prevent this Google App should be allowed to login user and direct him back to our app. According to this discussion: https://code.google.com/p/google-plus-platform/issues/detail?id=900 it is allowed by Apple.
So in my implementation firstly I am checking if the Google App is installed:
- (BOOL)openURL:(NSURL*)url {
NSURL *googlePlusURL = [[NSURL alloc] initWithString:#"gplus://plus.google.com/"];
BOOL hasGPPlusAppInstalled = [[UIApplication sharedApplication] canOpenURL:googlePlusURL];
if(!hasGPPlusAppInstalled)
{
if ([[url absoluteString] hasPrefix:#"googlechrome-x-callback:"]) {
return NO;
} else if ([[url absoluteString] hasPrefix:#"https://accounts.google.com/o/oauth2/auth"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationOpenGoogleAuthNotification object:url];
return NO;
}
}
return [super openURL:url];
}
EDIT:
Now I can confirm that my app was finally approved with this solution.
I hope it will help somebody.
It merges Google+ and Gmail samples and completely avoids using Google SignIn Button, i.e you do not leave the app.
Add both Google+ and Gmail API to you Google project, in your app login to google as you would to gmail using GTMOAuth2ViewControllerTouch.xib from OAuth2 and set scope to Google+:
-(IBAction)dologin{
NSString *scope = kGTLAuthScopePlusLogin;//Google+ scope
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientID
clientSecret:kClientSecret];
if ([auth refreshToken] == nil) {
GTMOAuth2ViewControllerTouch *authController;
authController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:scope
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:authController animated:YES];
}else{
[auth beginTokenFetchWithDelegate:self didFinishSelector:#selector(auth:finishedRefreshWithFetcher:error:)];
}
}
and RETAIN the authentication object if signed in successfully, then use that auth object when using google plus services:
GTLServicePlus* plusService = [[[GTLServicePlus alloc] init] autorelease];
[plusService setAuthorizer:self.auth];//!!!here use our authentication object!!!
No need for GPPSignIn.
Full write up is here: Here is Another Solution
Use the (new) Google Sign In iOS SDK.
The SDK natively supports Sign In through WebView when no Google app is present to complete the Sign In process. It also supports potential dispatch to several Google apps for this purpose.

Refresh values when application enters foreground

My application is using 'ALAssetsGroup' to keep track of the users albums
and i display these albums in my application
the user can click on the albums and see the images as well.
Now the problem arises when the user downloads images through safari or he alters the albums in any way .
my application still holds reference to the old albums instead of the new one
so i tried to refresh the albums using the
- (void)applicationWillEnterForeground:(UIApplication *)application
but the albums still seem to have the old value,
if i refresh them once more 'within' the application they get the correct values.
is there something else i can use to resolve this issue .. ?
maybe a notification .. ?
//This Registers a Notification for any changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didChangeLibrary:)
name:ALAssetsLibraryChangedNotification
object:[AGImagePickerController defaultAssetsLibrary]];
- (void)didChangeLibrary:(NSNotification *)notification
{
//Enter some code here to deal with the album changing
}
Edit: It seems like this does not work on iOS 5 (A radar has been opened for Apple, because this is a bug).
Here is a work around to use in the meantime:
Call
[self.assetsLibrary writeImageToSavedPhotosAlbum:nil metadata:nil
completionBlock:^(NSURL *assetURL, NSError *error) { }];
immediately after creating instance of ALAssetsLibrary Observe
ALAssetsLibraryChangedNotification (not
NSManagedObjectContextObjectsDidChangeNotification)
Use the applicationDidBecomeActive method in the ApplicationDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}

FBConnect's handleOpenURL Method Not Called by my AppDelegate

I have implemented the FBConnect SDK into my app, and it works perfectly on the simulator. I then modified my app's .plist file appropriately, and added the necessary method to my AppDelegate for when Facebook is installed on the device:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"handleOpenURL Method was called and sent the following:");
NSString *urlString = [NSString stringWithContentsOfURL:(NSURL *)url];
NSLog(#"URL String: %#", urlString);
return [[flipsideViewController facebook] handleOpenURL:url];
}
From the above NSLogs and the observation that my application is returned to the foreground after authorizing access via Facebook, I infer that the FB App is handing off control to my application appropriately. Unfortunately, Facebook.m's "handleOpenURL:url" method does not actually get called as I request in my AppDelegate (i.e. neither of the below NSLogs are displayed).
- (BOOL)handleOpenURL:(NSURL *)url {
// If the URL's structure doesn't match the structure used for Facebook authorization, abort.
NSLog(#"handleOpenURL was handled by SDK. Good!");
if (![[url absoluteString] hasPrefix:[self getOwnBaseUrl]]) {
NSLog(#"handleOpenURL structure doesn't match the structure used for Facebook authorization. Aborting.");
return NO;
}
//...
As a result, my app's view controller (where I clicked my facebook button to begin with) is simply returned to the screen, and the code I placed in the 'fbDidLogin' method (used in my view controller to publish to the user's wall) never gets called as it did in the simulator.
What am I overlooking? What, if any, other information is needed to solve this problem? Any help would be very much appreciated, as I have been struggling with this for a while.
Important Summary Notes:
1.) Application runs as desired on simulator.
2.) handleOpenURL Method not called when I request it from my AppDelegate.
3.) I do not receive any errors/warnings.
4.) I can run the DemoApp on my device, and I notice that the handleOpenURL Method is called appropiately when requested by the 'DemoAppAppDelegate.'
Thanks in Advance!
I ended up having to make my facebook object a singleton (stored in the appDelegate). Not completely sure why this was necessary though, as I was not using the fb object in multiple viewControllers. Please comment if you know why this was necessary.
See the following links for more information:
handleOpenUrl and TabBar Application
Facebook SDK: Call from multiple viewControllers
Cocoa with Love: Singletons

Lauching App with URL (via UIApplicationDelegate's handleOpenURL) working under iOS 4, but not under iOS 3.2

I have implemented UIApplicationDelegate's
application:didFinishLaunchingWithOptions:
and
application:handleOpenURL:
according to specification, i.e.,
application:didFinishLaunchingWithOptions:
returns YES
and
application:handleOpenURL: opens the URL.
The code works under iOS 4 (in both cases, i.e., when the app is launched and when it becomes active from suspended state). However, the code does not work under iOS 3.2.
I give an answer to my own question. Finding out the solution took me a while and was quite frustrating. If you do an internet search you find some partial answers, but it still took me a while to work out the following solution and I do hope it adds some clarity.
So first, the recommended behavior of your app appears to be the following (see Opening Supported File Types in iOS Ref Lib):
Do not implement applicationDidFinishLaunching: (see the note at UIApplicationDelegate).
Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it.
Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.
In iOS 4, passing an URL to an app results in one of the following two behaviors:
If the app is launched then application:didFinishLaunchingWithOptions: is called and application:handleOpenURL: is called if and application:didFinishLaunchingWithOptions: returned YES.
If the app is becoming active from suspended state then application:didFinishLaunchingWithOptions: is not called but application:handleOpenURL: is called.
However, in iOS 3.2 it appears as if application:handleOpenURL: is never called! A hint that the behavior is different under iOS 3.2 can be found in Handling URL Requests. There you find that application:handleOpenURL: is called if application:didFinishLaunchingWithOptions: is not implemented, but applicationDidFinishLaunching: is implemented. But application:handleOpenURL: is not called if application:didFinishLaunchingWithOptions: is implemented.
Hence, one solution to make the code work under 3.2 and 4.0 is:
Open the URL in application:didFinishLaunchingWithOptions:, but then return NO to prevent that application:handleOpenURL: is called.
Open the URL in application:handleOpenURL:, in case you are under 4.0 and the app was in suspended state.
I found this solution in another post, but I was confused, because it contradicted the recommendation in iOS Ref Lib documentation (namely that we should return YES in application:didFinishLaunchingWithOptions:). (At that point I did not realize that the documentation contradicts it self).
I believe that the current iOS 4.0 behavior will be the future behavior I prefer the following solution:
Do not implement applicationDidFinishLaunching:.
Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it. If we are on 3.2, open the URL.
Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.
So in summary, I implement the iOS 4 behavior and added the following line to application:didFinishLaunchingWithOptions:
if([[[UIDevice currentDevice] systemVersion] hasPrefix:#"3.2"]) {
[self application:application handleOpenURL:url];
}
which make the code work under 3.2.
application:handleOpenURL: is now DEPRECATED.
As of iOS 4.2, you can use this for opening URLs:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
Documentation:
https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html
I started writing application which used Dropbox api. To understand concept, I ran a sample application using my Key/secret mentioned at dropbox/developer documentation.
Once sample app started working, I used same key/secret values for my application.
For sample app, implementation of handleOpenURL (or openURL on iOS 4.2) gets executed as expected. For some odd reason, it wasn't the case for my app. My app entered background in order to show login screen and authentication page of dropbox. After successful login and authentication, my app never entered foreground. It was true for both platform Simulator and device (iPad)
I tried almost everything listed on internet including this post. Thanks. There was NO success, though.
At last, it STARTED working for my application when I did following:
On simulator, select "iOS Simulator --> Reset Content and Settings", and reset.
On device, I deleted sample application related executable and which in turn delete cache associated to it.
Add the following to the end of application:DidFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
if (url != nil && [url isFileURL]) {
return YES;
} else return NO;
} // End of application:didFinishLaunchingWithOptions:
// New method starts
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
mvc = [nc.viewControllers objectAtIndex:0];
if (url != nil && [url isFileURL]) {
[mvc handleOpenURL:url];
}
return YES;
}
where mvc is my main ViewController, and nc my navigation controller.
Then in the MainViewController, do something like this:
- (void)handleOpenURL:(NSURL *)url {
[self.navigationController popToRootViewControllerAnimated:YES];
// Next bit not relevant just left in as part of the example
NSData *jsonData = [NSData dataWithContentsOfURL:url];
NSError *error;
NSDictionary *dictionary = [[NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error] objectAtIndex:0];
[self managedObjectFromStructure:dictionary withManagedObjectContext:self.context];
...
}
after declaring handleOpenURL in the .h of course.
Thanks goes to Christian for putting in the effort for this.