Creating/Invoking Objective-C Delegate Methods Objective-C - iphone

I have tried going through this Question: How Does A Delegate Work and I still don't seem to have a full grasp on it. I am trying to use the CocoaAsyncSocket library to create a TCP socket connection. Thanks to help from a very friendly SO user, I have the following code to perform a read data request to the server:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if(msg)
{
NSLog(#"RX:%#",msg);
}
}
Now, forgive my ignorance as I'm pretty new to iOS development. So now I have this method that I want to invoke which will perform my ReadData. The problem is, I do not know WHERE to put this method (I have several views, with several header/implementation files). I want this method to be a delegate method, but I do not know how to make it a delegate method. I want to invoke this delegate method from my view.
If anyone could explain:
Where do I put this code? (What file, etc)
How do I make this a delegate method?
How do I invoke this delegate method?
I've been stuck on this all day, and I'm about to throw it the towel lol. Any and all help is much appreciated. Thanks so much!
EDIT:
This is kind of a bridge from a previous question, but I don't think that question has too much relevance to this question. Question

Thanks for updating. It is now clearer.
Here are some answers. If it is not clear, please let me know.
- Where do I put this code? (What file, etc)
This is a delegate method of CocoaAsyncSocket. Back to your first question, when you initialized it, you set yourself (your appDelegate) as the delegate.
socket = [[AsyncSocket alloc] initWithDelegate:self];
That means, you will be called from another class. So that means this method should be in the same class where you initialized the object (here socket) and set it as the delegate. So it stays in appDelegate
- How do I make this a delegate method?
You don't. This is a delegate method itself.
- How do I invoke this delegate method?
You don't. Another class (here AsyncSocket) will invoke it.
You may now ask, how you pass the data to your viewControllers?
That depends on your design. Once this method is called and you get notified that there is a connection, and data is being read, depending on your design, you pass the data to other view controllers. One way is by using NSNotification. e.g.
// Call this in onSocket:didReadData:withTag: instead of logging
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataIsReadNotification" object:msg]
// In one of your view controllers
// View controllers insterested in this message, register to get notified:
// add to -viewDidLoad
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateViewWithNotification:) name:#"DataIsReadNotification" object:nil];
...
// and somewhere in the view controller class implement this
- (void)updateViewWithNotification:(NSNotification *)notification {
NSString *msg = [notification object];
}

To make a delegate you would declare that stuff in the header file. Example below.
//SomeClass.h
#protocol SomeClassDelegate <NSObject>
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate>delegate;
}
#property(nonatomic,assign)id<SomeClassDelegate>delegate;
#end
Now when you initialize SomeClass, you can set the delegate to whatever self is. Now In your SomeClass file, you can do
[self.delegate onSocket: .. didReadData: .. withTag:];
If the method exists in the class you set as the delegate to SomeClass, it will call it. Hope this helps.

I am not familiar with the - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
method but using delegate in iOS is very common way to have your objects communicate between each others without introducing some dependencies.
Here are few hints to answer your 3 bullets questions:
Where do I put this code?
The best bet will be within the controller you want your ReadData to be performed or in the appDelegate
How do I make this a delegate method?
If this method has been defined in a protocol this is already by definition a delegate method. You just need to have your viewcontroller class to conform to this protocol and make your controller the delegate for this method.
How do I invoke this delegate method
You don't invoke a delegate method. You have an other object that belongs to this CocoaAsyncSocket library which will make a call to this delegate method. You simply take care of handling some code within the delegate method for your controller because you have defined it as the object who will handle this method.
Just see a delegate as a way to defer some work to an other object (it is a design pattern btw)

Related

How does a delegate method know when to be called

I'm just wondering how exactly does a delegate method know when to be called? For example in the UITextFieldDelegate protocol the textFieldDidBeginEditing: method is called when editing begins in the textfield (provided I implemented this method).
So how exactly does the code to detect when to call textFieldDidBeginEditing:? Does the system just check if textFieldDidBeginEditing: is already implemented and if it is it runs that method? Is there something under the hood that I'm not seeing?
Exactly.
I can't vouch for how Apple's framework code is implemented under the hood, but an exceedingly common refrain is:
if ([[self delegate] respondsToSelector:#selector(someInstance:didDoSomethingWith:)]) {
[[self delegate] someInstance:self didDoSomethingWith:foo];
}
This allows you to have optional delegate methods, which appears to be your question.
The code doesn't 'detect when to call' a delegate method. The textField receives an event, and calls the method on it's delegate (which has the textFieldDidBeginEditing: method implemented).
In short, when you tap the textfield to start editing, the textField says 'oh, I'm editing now!' and internally calls [self.delegate textFieldDidBeginEditing:self], where the delegate is the instance in which you've set to be the delegate (usually a UIViewController subclass)

Is there a way to send an object via Delegates in Objective-C

I have a scenario, where I am waiting for a uiwebview to get fully loaded and then a delegate called from finishedWebViewLoading once completed. However, I want to return that webView when finished loading as an object which is a private object to another Class. Is there anyway to do that through delegates? I don't want to have that uiwebview as a public property, hence I have utilized it under my main file.
Thanks.
sure you can do it like this .. in .h
#protocol yourClassProtocal <NSObject>
- (void) loadingFinsihForWebView:(UIWebView*)wv;
#end
and in the webView Delegate webViewDidFinishLoad
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[delegate loadingFinsihForWebView:webView];
}
I am not sure I understand what you are asking, but you cannot return any value from (void)webViewDidFinishLoad:(UIWebView*)webView, nor can you change the delegate signature.
On the other hand, if you mean that you are going to call another custom delegate method from webViewDidFinishLoad, and that you want that it returns the webView, then this is definitely possible. Simply call that custom delegate method and pass the webView to it, then make the custom delegate method return it.
This assumes you are in charge of the definition of the custom delegate method signature, e.g.:
#protocol CustomDelegateProtocol
- (UIWebView*)myCustomDelegateMethod:(UIWebView*)view;
#end
The answer to your question is: YES.
However, I believe you might need to read up on how delegates work and how to create them, this post might get you on the right path getting there: How does a delegate work in objective-C?
Also, have a look at this article: http://blog.shoguniphicus.com/2011/10/17/writing-custom-delegate-in-objective-c/
You can use this:
if([self.theDelegate respondsToSelector:#selector(loadData:)]) {
[self.theDelegate performSelector:#selector(loadData:) withObject:theObject];

Calling method in current view controller from App Delegate in iOS

I have two view controllers (BuildingsViewController and RoomsViewController) that both use a function within the App Delegate called upload. The upload function basically does an HTTP request, and if its successful or unsuccessful, triggers a uialertview. This is working fine.
The part I'm struggling with is from within the app delegate's connectionDidFinishLoading method. I need to be able to basically refresh the current view controller via perhaps viewWillAppear method of that view controller. Inside the viewWillAppear function of each view controller I have code which determines the buttons on the bottom toolbar.
I want the "upload" button in the toolbar of each view controller to automatically be removed when the uploading is done via the app delegate.
I've tried doing [viewController viewWillAppear:YES] from within the connectionDidFinishLoading method of the app delegate, but it never gets called.
I hope I'm clear enough. Any help is greatly appreciated.
Thanks.
To do the refresh of the view do not call viewWillAppear if the view is already displayed. What you want to do is the following:
When ConnectionDidFinishLoading method is triggered post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"refreshView" object:nil];
In your viewController observe for this notification. You do it by adding this code to your init or viewDidLoad method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView:) name:#"refreshView" object:nil];
Now implement -(void)refreshView:(NSNotification *) notification method in your viewController to manage your view to your liking.
If you are targeting iOS 4.0 and later, you can use the window's rootViewController property to get the current view controller.
[window.rootViewController viewWillAppear];
If you want your application to run on versions prior to iOS 4.0, then you could add an instance variable to the application delegate to remember which view controller called the upload method, having the controller send itself as a parameter.
- (void)upload:(UIViewController *)viewController {
self.uploadingViewController = viewController; // This is the property you add
...
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.uploadingViewController viewWillAppear];
self.uploadingViewController = nil;
}
You should also consider using a different method to reload the buttons, something like reloadButtons, since it is not related to the view appearing in this case. You would then call that method from within viewWillAppear.
Step 1:
In your App Delegate .h file you need to declare a protocol like so:
#protocol AppConnectionDelegate <NSObject>
#required
-(void)connectionFinished:(NSObject*)outObject;
#end
In the same file, add an ivar like so:
id *delegate;
Declare the ivar as a property:
#property (nonatomic, assign) id<AppConnectionDelegate> delegate;
In the App Delegate .m file, synthesize the ivar:
#synthesize delegate;
In the App Delegate .m file, on connectionDidFinishLoading do:
if([self.delegate respondsToSelector:#selector(connectionFinished:)])
{
[self.delegate connectionFinished:objectYouWantToSend];
}
In your viewcontroller's .h file, implement the AppConnectionDelegate by importing a reference to the app delegate file:
#import "AppDelegate_iPhone.h" //if using iPhone
#import "AppDelegate_iPad.h" //if using iPad
In the same file, at the end of the first line of the interface declaration do:
#interface AppDelegate_iPhone : AppDelegate_Shared <AppConnectionDelegate>
Declare ivars accordingly:
AppDelegate_iPhone *appDelegate; //if using iPhone
AppDelegate_iPad *appDelegate; // if using iPad
In your viewcontroller's .m file in the viewDidLoad(), get a reference to your app delegate using:
If iPhone;
appDelegate = (AppDelegate_iPhone*)[[UIApplication sharedApplication] delegate];
If iPad:
appDelegate = (AppDelegate_iPad*)[[UIApplication sharedApplication] delegate];
Then set the viewcontroller to be the delegate in viewDidLoad() by doing:
appDelegate.delegate = self;
Now you need to simply implement the connectionFinished method in the .m file:
- (void)connectionFinished:(NSObject*)incomingObject
{
//Do whatever you want here when the connection is finished. IncomingObject is the object that the app delegate sent.
}
Now whenever your app delegate's connectionDidFinishLoading is called, the view controller will be notified.
[It's a best practice to set appDelegate.delegate = nil if you're done using the connectionFinished callback]
This is tried and tested. If you have questions, leave a comment......
--EDIT--This is a robust alternative to NSNotification. I use both depending on the requirements. The process I use to decide between using NSNotification or a delegate callback using a protocol is simply:
For notifications:
One sender, multiple listeners.
No reference possible between sender and listener.
Complex/multiple objects need not be sent
For delegate callbacks using protocols:
One sender, limited (usually 1) listeners.
A reference between sender and listener is possible.
Complex/multiple objects are to be sent (for example, response objects that need to be sent)
I know sending objects is possible through notifications but I prefer protocols for that.
--EDIT--
Worse comes to worst, you can have both view controllers adhere to a simple one method protocol that will remove that button and refresh the view. Then in your connectionDidFinishLoading method, since you know your view controller must adhere to that protocol, by your design, you simply do something like
ViewController<MyProtocol> curView = (Get the current view controller somehow);
[curview refreshView];

Is it possible to subclass UiApplicationDelegate protocol?

To be honest I don't know how to call it, so I'll try to describe it.
UIApplicationDelegate protocol has "application:handleOpenURL:" method. And if I implement this method in my ApplicationDelegate class, it will be called when somebody opens my urls.
details:
http://developer.apple.com/iphone/library/documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:handleOpenURL:
However, I'd like my other class (uiviewcontroller) to receive this call. To make a different example - you can create a few classes and each of them can get GPS position. Is it possible to do the same with UIApplicationDelegate protocol?
I searched a lot for this topic here, but I couldn't find any answer on how to do it. I know how to get my application delegate ([[UIApplication sharedApplication] delegate]), but it's not the case in this situation.
You can always tell somebody who came to objective-c from some other object oriented language, because their first instinct is to subclass, subclass, subclass. There's not a lot of subclassing in obj-c. You CAN, obviously, but it's not how things are conventionally done, especially with things that are as one-shot-ish as UIApplicationDelegate. The more Cocoaish Way is to use categories, or sometimes to create a new NSObject subclass that contains the would-be parent class as a property.
In this case, for sure subclassing is a bad idea. the UIApplication singleton can only have one delegate property. So if you create a new UIApplicationDelegate, you've got no place to hook to it.
Instead, smarten up your one delegate's application:handleOpenURL: method to catch the URL call and load up whichever UIViewController subclass (I know, I know: exceptions) is going to handle it.
The simplest solution would be to use an NSNotification. This will allow you to handle the handleOpenURL call wherever you need to without creating any unnecessary coupling between your application delegate and the class you want to handle it.
In your app delegate, handle the delegate method and forward the data on using NSNotificationCenter.
- (void)application:(UIApplication *)application handleOpenURL:(NSURL *)URL
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyApplicationHandleOpenURLNotification" object:self userInfo:[NSDictionary dictionaryWithObject:URL forKey:#"URL"]];
}
Now, wherever you need to handle this, simply register as an observer for the notification and pull the URL out of the notification user info dictionary.
Judging by your comments to Dan Ray's answer, it sounds like you are looking for something like Three20's URL-based navigation

Implementing Delegate Pattern in Objective-C

I am building a class that handles NSURLConnection requests. To allow other classes to use this class, I would like to allow the main class to call a delegate when connectionDidFinishLoading is fired.
I've looked through lots of documentation, but I can't find anything that gives any clear examples, and the code that I have doesn't call the delegate for some reason. The code I have so far is (code not relevant removed):
Interface:
#interface PDUrlHandler : NSObject {
id delegate;
}
- (void)searchForItemNamed:(NSString *)searchQuery;
#property (nonatomic, assign) id delegate;
#end
#interface NSObject (PDUrlHandlerDelegate)
- (void)urlHandler:(PDUrlhandler*)urlHandler searchResultsFinishedLoading:(NSDictionary *)resultData;
#end
Implementation:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Fininshed Loading...");
resultData = [self parseJSON:jsonData];
if(delegate && [delegate respondsToSelector:#selector(urlHandler:searchResultsFinishedLoading:)]) {
NSLog(#"Delegating!");
[delegate urlHandler:self searchResultsFinishedLoading:resultData];
} else {
NSLog(#"Not Delegating. I dont know why.");
}
}
The delegate within the other class:
- (void)urlHandler:(PDUrlhandler*)urlHandler searchResultsFinishedLoading:(NSDictionary *)resultData;
{
NSLog(#"Delegating!!!!");
}
My first thought was you might not have set the delegate, but you have. Other than that, the code looks correct. Can't see anything wrong. Did you try and put a breakpoint at the place where you are checking whether your delegate responds to a selector? Could be that the delegate value was not retained and became nil. Make sure your delegate is not nil and has the correct object.
Also are you sure the connection is asynchronous? Synchronous connections will not call the connectionDidFinishLoading method
Turns out I forgot to set the delegate:
[currentHandler setDelegate:self];
needed to go after the line that makes the initial call to the PDUrlHandler.
For anyone interested in seeing an example of this the apple sample application NSURLCache implements a simple delegate around an NSURLConnection in NSURLCacheConnection.m
The sample app is available through the apple developer connection here:
http://developer.apple.com/iphone/library/samplecode/URLCache/index.html
I found it pretty useful.
You're on the right track, the way you're implementing the delegate pattern looks okay. The reason it's not being called is because you're using the wrong method signature in respondsToSelector; you have humidorServer:searchResultsFinishedLoading: when you actually want urlHandler:searchResultsFinishedLoading:.
Could it be the semi colon at the end of the method name in the delegate (bottom code sample on the far right)? If the delegate is set and the -connectionDidFinishLoading: method is being called then I can't see anything wrong
Since you tagged this with "iphone", I assume you're working on an iPhone app and don't need to support OS X pre-10.5. In Objective-C 2.0, Apple suggests you use formal protocols (using #protocol) with optional methods instead of informal protocols. Here's the relevant text:
Being informal, protocols declared in
categories don’t receive much language
support. There’s no type checking at
compile time nor a check at runtime to
see whether an object conforms to the
protocol. To get these benefits, you
must use a formal protocol. An
informal protocol may be useful when
all the methods are optional, such as
for a delegate, but (on Mac OS X v10.5
and later) it is typically better to
use a formal protocol with optional
methods.
(source)