Move Delegates to their own classes? - objective-c

My view controller is getting a little large for me. I'm implementing five delegate protocols and was about to add a sixth.
ABCViewController : UITableViewController<NSFetchedResultsControllerDelegate,
UITableViewDelegate,
UITableViewDataSource,
UIAlertViewDelegate,
CLLocationManagerDelegate>
One controller to implement them all seems ridiculous, but they aren't being used anywhere else. Should these be in their own classes or in the view controller?

You could add categories to ABCViewController, like this:
1. Move any declarations in ABCViewController.m into a private category in ABCViewController.h
// in ABCViewController.h
#interface ABCViewController : UIViewController <delegates>
// anything that's in the _public_ interface of this class.
#end
#interface ABCViewController ()
// anything that's _private_ to this class. Anything you had defined in the .m previously
#end
2. ABCViewController.m should include that .h.
3. Then in ABCViewController+SomeDelegate.h and .m
// in ABCViewController+SomeDelegate.h
#interface ABCViewController (SomeDelegateMethods)
#end
// in ABCViewController+SomeDelegate.m
#import "ABCViewController+SomeDelegate.h"
#import "ABCViewController.h" // here's how will get access to the private implementation, like the _fetchedResultsController
#implementation ABCViewController (SomeDelegateMethods)
// yada yada
#end

You can also declare conformity to that protocol in the .m file like this:
#interface ABCViewController (NSFetchedResultsControllerDelegateMethods) <NSFetchedResultsControllerDelegate>
#end
#implementation ABCViewController (NSFetchedResultsControllerDelegateMethods)
...
#end
This won't make your file shorter but at least it will be clearly divided into parts
If you are using Xcode you can try something like this for example:
#pragma mark - NSFetchedResultsControllerDelegateMethods
Quite handy to find your methods like in this tip: Pragma mark
Alternatively, depending on what you do the delegate methods and how structured is your code you could have another object that has only methods of the delegate protocol
#interface Delegate <NSFetchedResultsControllerDelegate> : NSObject
#end
You would have an instance of this object as an ivar in your ABCViewController.

Related

Using #class to get access to a delegate protocol declaration

I've read that you should try to use #class in your header file instead of #import but this doesn't work when your #class contains a delegate protocol that you're trying to use.
MyView.h
#import <UIKit/UIKit.h>
#class MyCustomClass; // <-- doesn't work for MyCustomClassDelegate, used below
#interface MyView : UIView <MyCustomClassDelegate>
#end
I think I'm overlooking something, is there a way to get #class to work in this situation or is #import my only choice?
Edit: One work around for this is, of course, declaring your #import MyCustomClass and MyCustomClassDelegate in the private interface section of the .m file instead of the .h file.
you can use #protocol to forward declare a protocol if you only need it for variables such as this:
#protocol MyProtocol;
#interface MyClass {
id<MyProtocol> var;
}
#end
In your case the declared class is trying to conform to a protocol so the compiler must know about the protocol methods at this point in order to deduce weather or not the class conforms.
In this case, I think your options are to split the protocol into its own file and #import that header, or declare the protocol in that header above the class declaration that uses it.
You can only forward-declare a protocol in the same header file for usage in method return values or parameter types. In your case you want the class to conform to the protocol, so it won't work since it defines behavior that will be added to the class itself (i.e. the methods it will respond to).
Therefore, you must #import the protocol. For this reason, it is probably a good idea to split the protocol and class up into separate files. See this answer for more information.
MyCustomClassDelegate is a protocol, not a class. Telling the compiler about the existence of MyCustomClass tells it nothing about the existence of the protocol.
You will need to declare your delegate protocol before the class:
MyCustomClass.h:
#import <UIKit/UIKit.h>
#class MyCustomClass;
#protocol MyCustomClassDelegate <NSObject>
- (void)myCustomClass:(MyCustomClass *)customClass
didBlah:(BOOL)blah;
#end
#interface MyCustomClass : NSObject <MyCustomClassDelegate>
#end
And you cannot even use #protocol to forward-declare the delegate protocol; the compiler must see the complete declaration, therefore change your #class for an #import:
MyView.h:
#import <UIKit/UIKit.h>
#import "MyCustomClass.h" // the compile now knows what MyCustomClassDelegate is
#interface MyView : UIView <MyCustomClassDelegate>
#end
You cannot forward declare a protocol you conform to.
If you are using MyView as a MyCustomClassDelegate only in MyView's implementation, you can use Extension in MyView's .m file, such as this:
#import "MyView.h"
#import "MyCustomClassDelegate.h"
#interface MyView () <MyCustomClassDelegate> {
}
#end
#implementation MyView
...
#end

Having two #interfaces in the same .h file

I am making a photo upload to Facebook app and I think I need two #interfaces in my .h file for my View Controller.
Here is my ViewController.h file.
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#interface FirstViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
UIImagePickerController *bailey;
UIImagePickerController *baileys;
UIImage *image;
IBOutlet UIImageView *imageView;
}
- (IBAction)TakePhoto;
- (IBAction)ChooseExisting;
#end
#interface FirstViewController : UIViewController { SLComposeViewController *slComposeViewController;
UIImage *image; }
- (IBAction)ShareFB;
#end
When I try to build this code on to my iPhone or a Emulator it says
/Users/Condrum/Desktop/project/myApp/myApp/FirstViewController.h:21:1: Duplicate interface definition for class 'FirstViewController'
Thanks in advance for the help.
-Condrum.
The pattern is to put a single public interface into the .h file:
#interface FirstViewController : UIViewController
// in here put those public properties and method declarations that
// other classes need to have access to
#end
Then put the second #implementation in the .m file as a private class extension:
#interface FirstViewController () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
// in here, place those private properties and instance variables that
// only this class needs to be aware of
#end
Note, this second interface uses the () syntax, which indicates that the interface is extending a previously defined interface.
But there's no point in putting both of those interfaces in the same .h file (why have two interfaces; it would be more logical to combine them into one). The primary value of the private class extension is you can extend your interface with details that only the implementation cares about, and avoid cluttering your nice simple public interface. So generally, keep public interface in the .h file, and move the private stuff into the class extension in the .m file.
For more information, see Class Extensions Extend the Internal Implementation.

do you need to import .h files when using delegates?

-(IBAction)ok
{
//send message to the delegate with the new settings
[self.delegate setHeight:_height Width:_width Mines:_mines];
[self.delegate dismissViewControllerAnimated:YES completion:nil];
}
the first message to the delegate wouldn't work until i imported ViewController.h, but the second one worked without the import.
if i add -(void)setHeight:(int)h Width:(int)w Mines:(int)m; as required in the optionsViewController protocol will that mean that i no longer have to import the root .h file.
i intend to use delegation to send messages in other parts of the program so i want to make sure i am using it correctly and not importing things when i don't need to.
Thank you.
if i add -(void)setHeight:(int)h Width:(int)w Mines:(int)m; as required in the optionsViewController protocol will that mean that i no longer have to import the root .h file.
Yes! You could also add it as #optional and it would work (remember to check if the delegate -respondsToSelector: in that case). The whole idea is that your object regularly knows nothing about the delegate object - except that it conforms to the protocol (ie implements the #required and possibly the #optional methods).
Added for clarification (on my phone, which is a pain in the butt):
//OptionsViewController.h
//this object does NOT have to import
//the calling viewControllers .h file
//which is what I think the OP does
#protocol optionsViewControllerProtocol;
#interface OptionsViewController : UIViewController
#property (nonatomic, assign) id<optionsViewControllerProtocol> delegate; //should be id, could be UIViewController too, if absolutely necessary (better design to make it id)
#end
#protocol optionsViewControllerProtocol <NSObject>
#required
-(void) setHeight: (NSInteger) height;
#end
//viewController.h
#import "optionsViewController.h" //necessary to get the protocols definitions
#interface OptionsViewController: UIViewController <optionsViewControllerProtocol>
//.....
If you define your delegate property to be of class UIViewController*, then the compiler will recognize the dismissViewControllerAnimated:completion: method without you needing to import anything, since that's a standard method for that class.
For a custom method, i.e. setHeight:Width:Mines:, you absolutely need to import the header file, or have it imported somewhere up the import chain.
Example: You have MyProtocol.h, and you want SomeClass to have a delegate property that conforms to that protocol. If you #import "MyProtocol.h" in SomeClass.h, you don't need to re-import it in SomeClass.m.
// SomeClass.h
#import "MyProtocol.h"
#interface SomeClass : NSObject
#property (weak, nonatomic) id<MyProtocol> delegate;
#end
//SomeClass.m
#import "SomeClass.h"
#implementation SomeClass
- (void)someMethod
{
[self.delegate myProtocolMethod];
}
#end

Proper way to create friend properties

All-
What is the proper way to define friend properties in Obj-C (specific implementation is Xcode for IOS). By friend properties, I mean instance attributes that are available to the base class, any subclasses of that base, but not to the public.
example:
#interface Base : NSObject
#property int friend
#end
#interface Sub : Base
#end
#implementation Base
#synthesize friend;
#end
#implementation Sub
-(id)newFriend
{
[self setFriend: [someOtherObject friend]]; // this should be allowed
}
#implementation Wow
-(void)
{
Sub* sub = [[Sub alloc] init];
[sub setFriend: [someOtherObject friend]]; // this should not be allowed
}
I have tried putting the #property friend for Base in the .m file, but then Sub cannot see it.
In c++ I had a helper declarator called friend which did exactly what I am looking for. How to do this in Obj-C?
Thanks
There's no way to enforce this behaviour in Objective-C. Properties are just getter and setter methods on the Objective-C object, and methods can be called by anyone.
One thing you can control is visibility. The way that I've emulated protected properties in the past is to declare only public properties in Base.h. Then create another header file called Base+protected.h, which contains a class extension with the declared property.
// Base.h
#interface Base : NSObject
#end
// Base+protected.h
#import "Base.h"
#interface Base ()
#property int friend;
#end
// Base.m
#import "Base.h"
#import "Base+protected.h"
#implementation Base
#synthesize friend;
#end
// Sub.h
#import "Base.h"
#interface Sub : Base
#end
// Sub.m
#import "Base+protected.h"
#implementation Sub
#end
So the Sub implementation can see the protected properties, but any external class only #includes Base.h or Sub.h, so cannot see them.

Error when declaring delegate property

This should be an easy question - but I'm having a hard time figuring it out. I'm trying to create a property on an object so that during prepareForSegue I can tell the object what it's delegate is. I know I could do this with protocols but I figured for this case a direct approach would be simplest. Unfortunately, the following code results in a compiler error:
#import <UIKit/UIKit.h>
#import "PlanningViewController.h"
#interface DepartmentViewController : UITableViewController
#property (nonatomic, weak) PlanningViewController *planningDelegate;
#end
When I type the property declaration, Xcode recognizes PlanningViewController and even displays the text for me to just tab through. The compiler, though, complains with:
Unknown type name 'PlanningViewController': did you mean 'UISplitViewController'?
What am I doing wrong?
PlanningViewController.h looks like this:
#import <UIKit/UIKit.h>
#import "DepartmentViewController.h"
#interface PlanningViewController : UITableViewController
// Table cell connections
- (IBAction)addItemPressed:(id)sender;
#end
Remove this line from your PlanningViewController.h header file:
#import "DepartmentViewController.h"
You have something of a loop in your header files.
Better still, make DepartmentViewController.h look like this (there is no need to include PlanningViewController.h in your header file):
#import <UIKit/UIKit.h>
#class PlanningViewController;
#interface DepartmentViewController : UITableViewController
#property (nonatomic, weak) PlanningViewController *planningDelegate;
#end
I think you've kind of missed one of the main points of the delegate patter which is to decouple you objects. The best way of declaring this delegate would be:
#import <UIKit/UIKit.h>
#protocol DepartmentViewControllerDelegate; // forward declaration of protocol
#interface DepartmentViewController : UITableViewController
#property (nonatomic, weak) id <DepartmentViewControllerDelegate> delegate;
#end
#protocol DepartmentViewControllerDelegate
- (void)departmentViewController:(DepartmentViewController *)controller
isProcessingPeople:(NSArray *)people
#end
In your department view controller, you would then write something like this:
if ([self.delegate respondsToSelector:#selector(departmentViewController:isProcessingPeople:)]) {
[self.delegate departmentViewController:self isProcessingPeople:people];
}
And in your planning view controller, you would implement this method:
- (void)departmentViewController:(DepartmentViewController *)controller
isProcessingPeople:(NSArray *)people {
// do necessary work here
}
The example here is just an example of one message you can send to the delegate. You can add whatever you need, but this makes it so there is no coupling between your controllers. The planning view controller knows everything it needs to about the department controller, but the department controller doesn't need to know anything about the planning controller.
If you want to stick with what you have currently, just recognize that it's not really the delegate pattern, and you should probably rename your property.