3

I am trying to include Google signin SDK to my AppDelegate.m file and it throw this error.

Its like I can only define one instance of the method.

// Facebook SDK
- (void)applicationDidBecomeActive:(UIApplication *)application {
  [FBSDKAppEvents activateApp];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  return [[FBSDKApplicationDelegate sharedInstance] application:application
                                                        openURL:url
                                              sourceApplication:sourceApplication
                                                     annotation:annotation];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {

  return [RNGoogleSignin application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}

@end
Floern
  • 31,495
  • 23
  • 98
  • 115
miknonny
  • 101
  • 1
  • 7
  • so what does the other - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation contains? – hariszaman Feb 21 '16 at 14:27
  • @hariszaman. I Think its contains logic to open GoogleSignin. SDK. I am just trying to implement signin for react-native with no knowledge of objective c – miknonny Feb 25 '16 at 06:49

2 Answers2

9

I guess the answer could be quite simple:

- (BOOL)application:(UIApplication *)application 
            openURL:(NSURL *)url 
  sourceApplication:(NSString *)sourceApplication 
         annotation:(id)annotation 
{
    if([[FBSDKApplicationDelegate sharedInstance] application:application
                                                      openURL:url
                                            sourceApplication:sourceApplication
                                                   annotation:annotation])
    {
        return YES;
    }

    return [RNGoogleSignin application:application 
                               openURL:url
                     sourceApplication:sourceApplication 
                            annotation:annotation];
}

If you add more, return YES when any of the agents handles the openURL. Order is irrelevant. Also this method is deprecated (use -application:openURL:options: instead), so you need to avoid using -application:openURL:sourceApplication:annotation: if you want your app to survive future changes.

UPD: Use my solution if you "want it now", there's good points in gaussblurinc's solution as well, so if you want something for a long run - derive something from it for sure.

MANIAK_dobrii
  • 5,805
  • 3
  • 27
  • 53
  • it could be improved via 'services' instance/class, which handles these methods. – gaussblurinc Feb 21 '16 at 14:48
  • also, chain of responsibility would solve this problem – gaussblurinc Feb 21 '16 at 14:50
  • 1
    This could be improved in many ways, I just provide the simpliest answer here. You are free to add you answer with all this details. – MANIAK_dobrii Feb 21 '16 at 14:51
  • what about detecting ur schemes? like here http://stackoverflow.com/questions/20848913/how-to-handle-uiapplication-handleopenurl-with-multiple-urls – hariszaman Feb 21 '16 at 14:52
  • @hariszaman you could do it via 'Services', this plain solution only increase complexity and number of questions – gaussblurinc Feb 21 '16 at 14:53
  • @hariszaman, @gaussblurinc: I think that `FBSDKApplicationDelegate` as well as `RNGoogleSignin` are clever enough to validate all the inputs and act just right. So, I expect them to return NO if they did nothing and YES if they did consume the input. That sounds good here. Also, please, see the question and the problem asker has, it is pretty simple, yeah? That's why I think this answer really suits asker's needs. So, if you can prove my solution won't solve the problem - argument, I'l update/remove my answer. Have anything to add - provide your answer, everybody would appreciate. – MANIAK_dobrii Feb 21 '16 at 14:57
  • @MANIAK_dobrii, hah, 'solving a problem' - no, you do it and post answer, no questions about your 'solution'. it only seems that bad style inside answer will cause later questions. The main problem that miknonny faced with is extensive usage of application delegate methods, which your solution still use. And if you can solve later issues later, I am not sure that miknonny could solve these issues as fast as you can. – gaussblurinc Feb 21 '16 at 15:16
  • @gaussblurinc, well, ooook. Than we're all waiting for your answer with a "good style". – MANIAK_dobrii Feb 21 '16 at 15:23
1
@interface Services : NSObject<UIApplicationDelegate> // or ApplicationDelegateServices?

@property (strong, nonatomic, readonly) NSArray <id<UIApplicationDelegate>> *services;

@end

@interface Services()

@property (strong, nonatomic, readwrite) NSArray <id<UIApplicationDelegate>> *services;

@end
@class RNGoogleSignin;
@class FBSDKApplicationDelegate;
@implementation Services

- (void)setup {
    // put all items inside here
    //
    id<UIApplicationDelegate> googleApplicationDelegate = (<UIApplicationDelegate>)[RNGoogleSignin class];
    id<UIApplicationDelegate> facebookApplicationDelegate = (<UIApplicationDelegate>)[FBSDKApplicationDelegate sharedInstance];
    self.services = @[facebookApplicationDelegate, googleApplicationDelegate];

}
// services - create chain of responsibility for openURL scheme
// they only need to adopt to protocol canOpenURL and openURL
// each service is <UIApplicationDelegate>
- (NSArray <id<UIApplicationDelegate>>*) chainForSelector:(SEL)selector {
    return [self.services filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id<UIApplicationDelegate>  _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        return [evaluatedObject respondsToSelector:selector];
    }]];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    // find all that responds to selector
    // filteredArrayUsingPredicate
    NSArray *chain = [self chainForSelector:_cmd];
    // or put your custom logic here, because all items in delegate are good enough to perform selector
    for (id<UIApplicationDelegate> delegate in chain) {
        if([delegate application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation]) {
            return YES;
        }
    }
    return NO;
}

@end

@interface AppDelegate<UIApplicationDelegate>

@property (nonatomic, weak) id<UIApplicationDelegate> services;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    // check that services responds to delegate method
    // if ([services respondsToSelector:...])
    if ([self.services respondsToSelector:_cmd]) {
        return [self.services application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation];
    }

    return NO/YES; // your choice
}

@end

After all

You can delegate all AppDelegate methods to external classes.

Your trouble was 'extensive usage of appDelegate methods'.

It seems that nobody wants to cleanup app delegate in apps because 'it just works'. I just show you ( bad or good? don't know ) an example of chain of responsibility pattern and simple delegation.

Remember, you can delegate most of your AppDelegate methods to other objects/classes, for example, you can provide smart 'PushNotificationsRouter' that will handle all push notification-related methods.

gaussblurinc
  • 3,476
  • 8
  • 31
  • 61
  • Yeah, that's good, but still could be improved. I'd suggest to wrap all concrete implementations into an adapter, so it could be safer than that. Also, looks like that your solution violates the single responsibility or single reason to change principle, and, in the way it is currently is, relationship between what each 'service' do in a whole picture could be hard to understand in a long run (i.e. you check respondsToSelector, that means you assume adding responsibilities in a future). – MANIAK_dobrii Feb 21 '16 at 15:34
  • But at a whole, yes, your solution is a way to go and I'd prefer something more elegant from your view. Have my +. – MANIAK_dobrii Feb 21 '16 at 15:34
  • 1
    Also, note, that people just copy/paste your code and then comply they get stupid errors. This is just how this world rolls. That's pretty good anyway, that you address those architectural issues, but the thing is that those who need that ask accordingly, those who don't (like, I think, in this case) might not appreciate your efforts. So I support your intents, just don't be impolite to others. – MANIAK_dobrii Feb 21 '16 at 15:45
  • 1
    @MANIAK_dobrii, just vote up your comment :) I believe in brave new world, where only beta and alpha could code, if you read it ;) – gaussblurinc Feb 21 '16 at 15:50