Subclass Object with pre implemented delegate method

delegates, ios, objective-c, subclass

Solution

What you can do is write an `NSProxy` subclass that implements `respondsToSelector:`. Something like this:

URLConnectionProxyDelegate.h:

#import <Foundation/Foundation.h>

@interface URLConnectionProxyDelegate : NSProxy <NSURLConnectionDelegate>

- (instancetype)initWithDelegate:(id<NSURLConnectionDelegate>)delegate;

@end

URLConnectionProxyDelegate.m:

#import "URLConnectionProxyDelegate.h"

@implementation URLConnectionProxyDelegate
{
    __weak id<NSURLConnectionDelegate> _realDelegate;
}


#pragma mark - Object Lifecycle

- (instancetype)initWithDelegate:(id<NSURLConnectionDelegate>)delegate
{
    if (self) {
        _realDelegate = delegate;
    }
    return self;
}


#pragma mark - NSProxy Overrides

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    return [(id)_realDelegate methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    invocation.target = _realDelegate;
    [invocation invoke];
}


#pragma mark - NSObject Protocol Methods

- (BOOL)respondsToSelector:(SEL)sel
{
    // replace @selector(connection:didFailWithError:) with your actual pre-implemented method's selector
    if (sel == @selector(connection:didFailWithError:)) {
        return YES;
    }

    return [_realDelegate respondsToSelector:sel];
}


#pragma mark - NSURLConnectionDelegate Methods

// Since I don't know which method your pre-implemented method is,
// I just chose connection:didFailWithError: as an example. Replace this
// with your actual pre-implemented method.
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"Connection failed: This gets called only when the proxy delegate is used");
}

@end

And then to use this class in, say, a view controller class of yours, you can do something like this:

SomeViewController.m:

#import "SomeViewController.h"
#import "URLConnectionProxyDelegate.h"

@interface SomeViewController () <NSURLConnectionDelegate>

@end


@implementation SomeViewController

#pragma mark - Button actions

- (IBAction)testSuccessURLWithNormalDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"http://example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Using self as the delegate
    [NSURLConnection connectionWithRequest:request delegate:self];
}

- (IBAction)testFailURLWithNormalDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"not a real url"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Using self as the delegate
    [NSURLConnection connectionWithRequest:request delegate:self];
}

- (IBAction)testSuccessURLWithProxyDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"http://example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Using a proxy delegate, with self as the real delegate
    URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self];
    [NSURLConnection connectionWithRequest:request delegate:proxy];
}

- (IBAction)testFailURLWithProxyDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"not a real url"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Using a proxy delegate, with self as the real delegate
    URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self];
    [NSURLConnection connectionWithRequest:request delegate:proxy];
}


#pragma mark - NSURLConnectionDelegate Methods

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"Connection failed: This gets called only when the view controller is used as the delegate");
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
    NSLog(@"Connection success: This gets called when the view controller OR the proxy delegate is used as the delegate");

    return YES;
}

@end

The important thing to note about all this is that `URLConnectionProxyDelegate` overrides `respondsToSelector:` and passes it along to its `_realDelegate` object instead of calling `super`, and it also always returns `YES` if the selector is your "pre-implemented" method's selector. This means you don't even have to implement any of the other methods in the `NSURLConnectionDelegate` protocol – you just need to implement the "pre-implemented" one.

You could even have several pre-implemented methods, as well. This is easily done by just adding more checks for the selector in `respondsToSelector:` of the proxy class:

[...]

if (sel == @selector(connection:didFailWithError:)) {
    return YES;
}
if (sel == @selector(connectionShouldUseCredentialStorage:)) {
    return YES;
}

[...]

... and then just making sure to implement all of those methods in the proxy class as well, of course:

[...]

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"pre-implemented connection:didFailWithError:");
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
    NSLog(@"pre-implemented connectionShouldUseCredentialStorage:");

    return YES;
}

[...]

Hope that makes sense and is of some help to you.

Problem

I'm trying to create a subclass of `NSURLConnection` which already has one delegate method pre implemented. My current approach is to use a "proxy" delegate which has this method pre filled and calls the other methods like this: ``` -(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{ if ([self.delegate respondsToSelector:@selector(connectionShouldUseCredentialStorage:)]) { return [self.delegate connectionShouldUseCredentialStorage:connection]; } else{ return NULL; } ``` } Where delegate is the actual user defined delegate. This causes somehow a problem because returning NULL in some cases causes the action to stop. What is the correct way to do this? My class should have in the end one preconfigured method called and the other stuff should be implemented by the dev. edit: Another addition what is the correct approach for a `void` delegate method? Edit2: another requirement is that the subclass should work like its parent but it must have one delegate method pre implemented. So the dev can additionally implement another delegates of NSURLConnection. Can't see how do this with a custom protocol

Original source