The idea it's very easy, i have an http download class, this class must support the http authentication but it's basically a background thread so i would like to avoid to prompt directly to the screen, i would like to use a delegate method to require from outside of the class, like a viewController.
But i don't know if is possible or if i have to use a different syntax.
This class use this delegate protocol:
//Updater.h
#protocol Updater <NSObject>
-(NSDictionary *)authRequired;
#optional
-(void)statusUpdate:(NSString *)newStatus;
-(void)downloadProgress:(int)percentage;
#end
#interface Updater : NSThread {
...
}
This is the call to the delegate method:
//Updater.m
// This check always fails :(
if ([self.delegate respondsToSelector:#selector(authRequired:)]) {
auth = [delegate authRequired];
}
This is the implementation of the delegate method
//rootViewController.m
-(NSDictionary *)authRequired;
{
// TODO: some kind of popup or modal view
NSMutableDictionary *ret=[[NSMutableDictionary alloc] init];
[ret setObject:#"utente" forKey:#"user"];
[ret setObject:#"password" forKey:#"pass"];
return ret;
}
if ([self.delegate respondsToSelector:#selector(authRequired:)]) {
In ObjC, the colons (:) in the method name is significant. That means authRequired and authRequired: are different methods. Try this instead:
if ([delegate respondsToSelector:#selector(authRequired)]) {
Related
Out of curiosity, is it possible to call the original delegate method implementation in a custom delegate implementation. like [super delegateMethod]; Or is that not possible. There are some scenarios where'd id like to add customizations to the behavior if certain conditions are met. Thank you in advance!
Yes, this can be achieved via a wrapper object which intercepts messages and forwards them to another delegate object. The following class intercepts calls to the scrollViewDidScroll: method before forwarding it (and any other delegate method invocations) to another UIScrollViewDelegate.
#import UIKit;
#interface ScrollHandler : NSObject <UIScrollViewDelegate>
- (instancetype)initWithScrollViewDelegate:(id<UIScrollViewDelegate>)delegate;
#end
#implementation ScrollHandler {
id<UIScrollViewDelegate> _scrollViewDelegate;
}
- (instancetype)initWithScrollViewDelegate:(id<UIScrollViewDelegate>)delegate {
self = [super init];
if (self) {
_scrollViewDelegate = delegate;
}
return self;
}
// Intercept specific method invocations:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// Implement custom behavior here before forwarding the invocation to _scrollViewDelegate
[_scrollViewDelegate scrollViewDidScroll:scrollView];
}
// Forward unintercepted method invocations:
- (BOOL)respondsToSelector:(SEL)selector
{
// Ask _scrollViewDelegate if it responds to the selector (and ourself as well)
return [_scrollViewDelegate respondsToSelector:selector] || [super respondsToSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// Forward the invocation to _scrollViewDelegate
[invocation invokeWithTarget:_scrollViewDelegate];
}
#end
Example use:
_scrollView = [[ScrollHandler alloc] init];
_scrollController = [[ScrollHandler alloc] initWithScrollViewDelegate:self];
_scrollView.delegate = _scrollController;
I have a view controller which defines a protocol which itself inherits another protocol.
I want any object that implements my protocol to also implement the inherited protocol.
I want to set my class to intercept some of the messages in the inherited protocol in order to configure some things internally but eventually would like to forward all of the messages to the delegate of my class
I could write a lot of boiler plate code to stub all of the protocol and intern call the delegate but I see that it breaks a lot of the time - any time the "super" protocol changes I need to restub this class once again.
I see that this is very predominant in custom UI controls. When reusing existing components - for instance tables or collection views you would like your data source to respond to all of the common protocols but some instances you need to configure the view according to the index or save a particular state.
I've tried using forwardingTargetForSelector in order to forward the messages I do not respond to , but it isn't always forwarding...
Here is a contrived code example:
Class A: (the top most protocol)
#
protocol classAProtocol <NSObject>
-(void)method1;
-(void)method2;
-(void)method3;
#end
My Class
#protocol MyClassProtocol <classAProtocol>
-(void)method4;
#end
#interface MyClass
#property (nonatomic,weak> id <MyClassProtocol> delegate;
#end
#interface MyClass (privateInterface)
#property (nonatomic,strong) ClassA *classAObject;
#end
#implementation MyClass
-(init)
{
self = [super init];
if (self)
{
_classAObject = [[ClassA alloc] init];
_classAObject.delegate = self; // want to answer some of the delegate methods but not all
}
}
-(void)method1
{
// do some internal configuration
// call my delegate with
[self.delegate method1];
}
-(id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self respondsToSelector:aSelector])
{
return self;
}
if ([self.delegate respondsToSelector:aSelector])
{
return self.delegate;
}
return nil;
}
-(void)setDelegate:(id <MyClassProtocol>)delegate
{
self.delegate = delegate; // will forward some of the messages
}
#end
Returning self from forwardingTargetForSelector: makes no sense because it would never be called if self responded to the selector.
You need to implement these three methods:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self.delegate respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:self.delegate];
else
[super forwardInvocation:anInvocation];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [self.delegate methodSignatureForSelector:selector];
}
return signature;
}
You shouldn't ever return self; from forwardingTargetForSelector:. Your check should mean it never is but if you ever did return self it would cause an infinite loop.
You need to be sure that a super class isn't implementing the method as this will prevent forwardingTargetForSelector: from being called. Check if the method is actually called.
forwardingTargetForSelector: is also only called when a method is called on your controller that it doesn't respond to. In your example you aren't calling [self ...];, you're calling [self.delegate ...]; so forwardingTargetForSelector: will not be called.
I have an Authenticator class which has a method to authenticate with an API key and another method to authenticate with an email address and password. Authentication is done with an async HTTP request.
I have a LoginController that is managing the view that the user will enter their email/password into.
Here's a code snippet:
// LoginController
- (void)awakeFromNib {
self.authenticator = [[Authenticator alloc] init];
}
- (IBAction)authenticateWithEmailAndPassword: (id)sender {
// Async HTTP request, so we can't just check the return
// value to see if authentication was successful or not
[self.authenticator authenticateWithEmail:[emailField stringValue]
password:[passwordField stringValue]];
}
My Authenticator object does the authentication, and my LoginController needs the asynchronous result (success or failure).
What's the Objective C way of communicating asynchronously from the model back to the controller?
One possible pattern you could use here would be a delegate.
You could define a AuthenticatorDelegate protocol and have your LoginController conform to it. Something like:
#protocol AuthenticatorDelegate
- (void) authenticationSuccessful;
- (void) authenticationFailedWithError:(NSError *)error
#end
#interface Authenticator : NSObject
{
...
id <AuthenticatorDelegate> delegate;
}
#property (assign) id <AuthenticatorDelegate> delegate;
#end
your LoginController would be declared as
#interface LoginController : UIViewController <AuthenticatorDelegate>
...
and when you initialize it you set the delegate
- (void)awakeFromNib {
self.authenticator = [[Authenticator alloc] init];
self.authenticator.delegate = self;
}
and then when your Authenticator objects receive the result you just call the appropriate method on the delegate.
Of course that's just one of the possibilities. Other approaches could use code blocks or a target/selector pair.
make authenticator the delegate of the your NSURLConnection, then override the
connectionDidFinishLoading:
and
connection:didFailWithError:
save a ref to LoginController in the authenticator, then when you get connectionDidFinishLoading: you evaluate the response and send the message back to
LoginController.
Let's say I need to communicate with a class that provides a protocol and calls delegate methods when an operation is complete, as so:
#protocol SomeObjectDelegate
#required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
#interface SomeObject : NSObject
{
}
#end
Now, I've decided that while I could make another class implement the stuffDone: delegate method, I've decided that I'd rather encapsulate the process into a block which is written somewhere close to where SomeObject is instantiated, called, etc. How might I do this? Or in other words, if you look at this famous article on blocks (in the Replace Callbacks section); how might I write a method in SomeObject that accepts a completionHandler: of sorts?
It sounds like you wish to communicate with an existing class which is designed to take a delegate object. There are a number of approaches, including:
using a category to add block-based variants of the appropriate methods;
use a derived class to add the block-based variants; and
write a class which implements the protocol and calls your blocks.
Here is one way to do (3). First let's assume your SomeObject is:
#protocol SomeObjectDelegate
#required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
#interface SomeObject : NSObject
{
}
+ (void) testCallback:(id<SomeObjectDelegate>)delegate;
#end
#implementation SomeObject
+ (void) testCallback:(id<SomeObjectDelegate>)delegate
{
[delegate stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
#end
so we have some way to test - you will have a real SomeObject.
Now define a class which implements the protocol and calls your supplied blocks:
#import "SomeObject.h"
typedef void (^StuffDoneBlock)(id anObject);
typedef void (^StuffFailedBlock)();
#interface SomeObjectBlockDelegate : NSObject<SomeObjectDelegate>
{
StuffDoneBlock stuffDoneCallback;
StuffFailedBlock stuffFailedCallback;
}
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)dealloc;
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
// protocol
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
#end
This class saves the blocks you pass in and calls them in response to the protocol callbacks. The implementation is straightforward:
#implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
// copy blocks onto heap
stuffDoneCallback = Block_copy(done);
stuffFailedCallback = Block_copy(fail);
}
return self;
}
- (void)dealloc
{
Block_release(stuffDoneCallback);
Block_release(stuffFailedCallback);
[super dealloc];
}
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
return (SomeObjectBlockDelegate *)[[[SomeObjectBlockDelegate alloc] initWithOnDone:done andOnFail:fail] autorelease];
}
// protocol
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
#end
The only thing you need to remember is to Block_copy() the blocks when initializing and to Block_release() them later - this is because blocks are stack allocated and your object may outlive its creating stack frame; Block_copy() creates a copy in the heap.
Now you can all a delegate-based method passing it blocks:
[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(#"Done: %#", anObject); }
andOnFail:^{ NSLog(#"Failed"); }
]
];
You can use this technique to wrap blocks for any protocol.
ARC Addendum
In response to the comment: to make this ARC compatible just remove the calls to Block_copy() leaving direct assignments:
stuffDoneCallback = done;
stuffFailedCallback = fail;
and remove the dealloc method. You can also change Blockcopy to copy, i.e. stuffDoneCallback = [done copy];, and this is what you might assume is needed from reading the ARC documentation. However it is not as the assignment is to a strong variable which causes ARC to retain the assigned value - and retaining a stack block copies it to the heap. Therefore the ARC code generated produces the same results with or without the copy.
You could do something like this:
typedef void (^AZCallback)(NSError *);
AZCallback callback = ^(NSError *error) {
if (error == nil) {
NSLog(#"succeeded!");
} else {
NSLog(#"failed: %#", error);
}
};
SomeObject *o = [[SomeObject alloc] init];
[o setCallback:callback]; // you *MUST* -copy the block
[o doStuff];
...etc;
Then inside SomeObject, you could do:
if ([self hadError]) {
callback([self error]);
} else {
callback(nil);
}
The below link explains how the call backs using delegates could be easily replaced with blocks.
The examples includes UITableview,UIAlertview and ModalViewController.
click me
Hope this helps.
I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-)
At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.
Given the following method signature
-(void)execute:(SEL)callback;
is there a way to enforce the signature for the selector passed to such a method?
The method is expecting a selector of a method with the following signature
-(void)foo:(NSData*)data;
But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the
execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.
The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.
One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.
In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:
FooController.h
#protocol FooControllerDelegate
#required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#interface FooController : NSObject
{
id <FooControllerDelegate> * delegate;
}
#property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
#end
FooController.m
#interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
#end
#implementation FooController
#synthesize delegate;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
delegate = nil;
...
return self;
}
- (void)doStuff
{
...
[self handleData:data];
}
- (void)handleData:(NSData *)data
{
if (delegate != nil)
{
[delegate handleData:data forFoo:self];
}
else
{
return;
// or throw an error
// or handle it yourself
}
}
#end
Using the #required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the #required protocol method will result in a compiler error.
Here is how you would create a delegate class to work with the above code:
#interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
// do something here
}
#end
And here is how you would use everything:
FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
// did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.
You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:
- (void)execute:(id)object
{
// Do the execute stuff, then...
if ([object respondsToSelector:#selector(notifyOnExecute:)]) {
[object notifyOnExecute:self];
}
// You could handle the "else" case here, if desired
}
If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.