IOS application - define protocol in separate file - objective-c

I have defined a protocol in a separate file (myProtocol.h). Here is the code for it:
#import <Foundation/Foundation.h>
#protocol myProtocol <NSObject>
-(void) loadDataComplete;
#end
Now I want to call this method so I have done the following code:
firstViewController.h:
#import "myProtocol.h"
#interface firstViewController : UIViewController{
id <myProtocol> delegate;
}
#property (retain) id delegate;
-(void) mymethod;
firstViewController.m
#implementation firstViewController
#synthesize delegate;
- (void)viewDidLoad {
[self mymethod];
}
-(void) mymethod {
//some code here...
[delegate loadDataComplete];
}
I have another file where the protocol is also utilized:
secondViewController.h:
#import "myProtocol.h"
#interface secondViewController : UIViewController<myProtocol>{
}
secondViewController.m:
-(void) loadDataComplete{
NSLog(#"loadDataComplete called");
}
but my secondViewController is not calling the protocol methad. Why is it so? Any suggestion will be appreciated.

First, as #Abizern suggested, try to reformat your code a little bit. Use capital letter for classes. Said this here the solution for your answer.
This is the protocol. I would name it like FirstViewControllerDelegate since the class that implements the object is a delegate for FirstViewController.
#import <Foundation/Foundation.h>
#protocol MyProtocol <NSObject>
- (void)doSomething;
#end
This is SecondViewController.
#import <UIKit/UIKit.h>
#import "MyProtocol.h"
#interface SecondViewController : UIViewController <MyProtocol>
#end
#implementation SecondViewController
// other code here...
- (void)doSomething
{
NSLog(#"Hello FirstViewController");
}
#end
This is FirstViewController.
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
// it coud be better to declare these properties within a class extension but for the sake of simplicity you could leave here
// the important thing is to not declare the delegate prop with a strong/retain property but with a weak/assign one, otherwise you can create cycle
#property (nonatomic, strong) SecondViewController* childController;
#property (nonatomic, weak) id<MyProtocol> delegate;
#end
#implementation FirstViewController
// other code here...
- (void)viewDidLoad
{
[super viewDidLoad];
self.childController = [[SecondViewController alloc] init];
self.delegate = self.childController; // here the central point
// be sure your delegate (SecondViewController) responds to doSomething method
if(![self.delegate respondsToSelector:#selector(doSomething)]) {
NSLog(#"delegate cannot respond");
} else {
NSLog(#"delegate can respond");
[self.delegate doSomething];
}
}
#end
For the sake of completeness, be sure to understand the delegate pattern means. Apple doc is your friend. You could take a look at the-basics-of-protocols-and-delegates to have a basic intro on the argument. Furthermore, SO search allows you to find a lot of answers on the topic.
Hope that helps.

Related

Subclass inherit Superclass Delegate

I have a hierarchy of UIViews. They are all handled differently but if nested I can not get my setDelegate of super to fire. I receive a crash exception [ThirdClass setDelegate:] unrecognized selector sent to instance. This actually happens no matter what (subclass) i use SecondClass or ThirdClass, but If I use (FirstClass) everything works as it should but any subclassing of the delegate it does not recognize the call. I have simplified what I am doing below which if I call out my first class separately inside my MainControlInterface everything works as it should. Im sure Im doing something wrong here but can't determine what that is, If anyone could help that would be greatly appreciated, thank you.
#protocol FirstClassDataSource, FirstClassDelegate;
#interface FirstClass : UIView
#property (nonatomic, weak_delegate) __nullable id<FirstClassDataSource> dataSource;
#property (nonatomic, weak_delegate) __nullable id<FistClassDelegate> delegate;
#end
#protocol FirstClassDataSource <NSObject>
- (NSInteger)doSomething:(FirstClass *)class;
#optional
- (NSInteger)doSomethingElse:(FirstClass *)class;
#end
#protocol FirstClassDelegate <NSObject>
#optional
- (void)handleMoreDelegateMethods:(FirstClass *)class;
#end
#implementation FirstClass
- (void)setDataSource:(id< FirstClassDataSource >)dataSource
{
if (_dataSource != dataSource)
{
_dataSource = dataSource;
if (_dataSource)
{
[self reloadData];
}
}
}
- (void)setDelegate:(id< FirstClassDelegate>)delegate
{
if (_delegate != delegate)
{
_delegate = delegate;
if (_delegate && _dataSource)
{
[self setNeedsLayout];
}
}
}
#end
#interface SecondClass : FirstClass
-(id)sencondClassesPrivateMethods;
#end
#interface ThirdClass : secondClass
-(id)thirdClassPrivateMethods;
#end
#interface MainControlInterface : UIView <FirstClassDataSource, FirstClassDelegate>
-(ThirdClass *)thirdClass;
#end
#implementation MainControlInterface
-(void)didMoveToSuperview{
ThirdClass *mythirdSubClass = [self thirdClass];
mythirdSubClass.delegate = self;
mythirdSubClass.dataSource = self;
}
#end
I can't tell what you're doing wrong either. But, your sample code will not compile. (It's full of typos.) I have tried to recreate what you're talking about, simplifying it further. (I've used CodeRunner, a macOS app which facilitates this sort of thing.)
#import <Foundation/Foundation.h>
#protocol FirstClassHandling <NSObject>
- (void)doTheThing;
#end
#interface FirstClass : NSObject
#property (nonatomic, weak) id<FirstClassHandling> delegate;
- (void)doSomething;
#end
#implementation FirstClass
- (void)doSomething
{
NSLog(#"First class.");
if ([[self delegate] respondsToSelector:#selector(doTheThing)]) {
[[self delegate] doTheThing];
}
}
#end
#interface SecondClass : FirstClass
#end
#implementation SecondClass
- (void)doSomething
{
NSLog(#"Second class");
[super doSomething];
}
#end
#interface Handler : NSObject <FirstClassHandling>
#end
#implementation Handler
- (void)doTheThing
{
NSLog(#"Doing my thing!!!");
}
#end
int main(int argc, char *argv[])
{
#autoreleasepool {
Handler* handler = [[Handler alloc] init];
SecondClass* sc = [[SecondClass alloc] init];
sc.delegate = handler;
[sc doSomething];
}
}
The above does not crash. Please fix your example code.

Obj-C, How do I use a category to supply methods which I will use in delegate methods?

I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
#interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
#property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
#end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
#implementation UIViewController (CloudKitDelegates)
#dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(#"%#", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
#end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: #"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
The problem: identical method crashLog: in multiple classes, for example
#interface ViewController : UIViewController
#end
#implementation ViewController
- (void)someMethod {
[self crashLog:#"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
Solution A: move crashLog: to a common superclass (or a category on superclass UIViewController)
#interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
#end
#implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[self crashLog:#"error"];
}
#end
Solution B: move crashLog: to a delegate and protocol
#protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
#end
#interface DelegateClass : AnyClass <ICloudDBDelegate>
#end
#implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : UIViewController
#end
#implementation ViewController
#property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:#"error"];
}
#end
#interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
#property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
#end
#implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
#end
Now we have new problem: property iCloudDBDelegate in multiple classes
Solution B + A: move crashLog to a delegate, move iCloudDBDelegate property to a superclass
#protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
#end
#interface DelegateClass : AnyClass <ICloudDBDelegate>
#end
#implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface CommonViewController : UIViewController
#property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
#end
#implementation CommonViewController
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:#"error"];
}
#end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults or NSFontManager.sharedFontManager: CloudDBManager.sharedCloudDBManager. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager from everywhere.
#interface CloudDBManager : NSObject
#property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
#end
#implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:#"error"];
}
#end
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."

Set text in the textfield does not work in Obj-C

I would like to set an text in the text field in another class and get it from another class. This is something what I want, but it does not work. Can you please help me. Thank you!
aaa.h
#import <Cocoa/Cocoa.h>
#interface aaa : NSImageView {
IBOutlet NSTextField *message;
}
#property (nonatomic, retain) IBOutlet NSTextField *message;
#end
aaa.m
#import "aaa.h"
#import "bbb.h"
#implementation aaa
#synthesize message;
- (void)awakeFromNib {
// [message setStringValue:#"ok, this works!"]; //but i don't want it from here
[self hello];
}
#end
bbb.h
#import <Foundation/Foundation.h>
#interface NSObject (bbb)
- (void)hello;
#end
bbb.m
#import "bbb.h"
#import "aaa.h"
#implementation NSObject (bbb)
- (void)hello {
aaa *obj = [[[aaa alloc] init] autorelease];
[obj.message setStringValue:#"This doesn't work :("]; // set text here, dont work.
NSLog(#"TEST: %#", [obj.message stringValue]);
}
#end
You are using category, so first thing it is used for extending the functionality of existing class. So you cannot set textfield value inside category. But else you can add some functonality after extracting the value. So you have to pass the value inside the category first. Try like this below:
- (void)awakeFromNib {
NSString *resultString=[self hello:#"This doesn't work :("];
[message setStringValue:resultString];
}
#end
#interface NSObject (bbb)
- (NSString*)hello:(NSString*)yourString;
#end
#implementation NSObject (bbb)
- (NSString*)hello:(NSString*)yourString {
return yourString;
}
#end

Protocol declaration not being found

So I have one class CommentViewController.h in which I have
#import "FirstViewController.h"
#protocol CommentViewControllerDelegate;
#interface CommentViewController : UIViewController {
id <CommentViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id <CommentViewControllerDelegate> delegate;
- (IBAction)submit:(id)sender;
-(IBAction)cancel:(id)sender;
#end
#protocol CommentViewControllerDelegate
-(void)commentViewControllerDidFinish:(CommentViewController *)controller;
#end
I synthesized delegate in the implementation
I try to access the protocol in FirstViewController.h:
#import "CommentViewController.h"
#interface FirstViewController : UIViewController <CommentViewControllerDelegate>
And in the implantation of FirstViewController :
- (void)commentViewControllerDidFinish:(CommentViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
The error appears on this line:
#interface FirstViewController : UIViewController <CommentViewControllerDelegate>
Error: Cannot find protocol declaration for 'CommentViewControllerDelegate'; did you mean 'UISplitViewControllerDelegate'?
Am I missing something? I always have trouble with protocols and delegates.
You have a loop in your include files.
Remove this line from CommentViewController.h:
#import "FirstViewController.h"
It's not referenced in that header file, and if it were, you could simply put:
#class FirstViewController;
instead of including the whole file.

own delegate - function work in 50%, something is wrong

i've got own delegate for my own class.
MainMenuViewDelegate
#import <Foundation/Foundation.h>
#class MainMenuView;
#protocol MainMenuViewDelegate <NSObject>
-(void) mainMenuViewLibrary:(MainMenuView*)controller withString:(NSString*)string;
#end
MainMenuView.h
#import <UIKit/UIKit.h>
#import "WorkspaceView.h"
#import "MainMenuViewDelegate.h"
#interface MainMenuView : UIView
#property (nonatomic,weak) id <MainMenuViewDelegate> delegate;
....
#end
MainMenuView.m
#implementation MainMenuView
#synthesize delegate;
...
-(void)library:(id)sender{
//test
NSLog(#"it's work");
NSString *string = #"some text";
[delegate mainMenuViewLibrary:self withString:string];
NSLog(#"finish");
}
WorkspaceView.h
#import <UIKit/UIKit.h>
#import "MainMenuViewDelegate.h"
#interface WorkspaceView : UIView <MainMenuViewDelegate> {
int menuStatus;
UILabel *label;
}
#property int menuStatus;
#property (nonatomic, retain) UILabel *label;
#end
WorkspaceView.m
#import "WorkspaceView.h"
#import "MainMenuView.h"
#implementation WorkspaceView
#synthesize menuStatus;
#synthesize label;
....
-(void) mainMenuViewLibrary:(MainMenuView*)controller withString:(NSString*)string{
[label setText: string];
}
#end
The problem appears when i press btnLibrary and invokes -(void)library:(id)sender function.
console print it's work, then my delegate function is invokes, but i don't see any change in my label (placed in WorkspaceView), and in finish console print finish.
None of your objects is set as the delegate so the delegate is nil - messages sent to nil are silenced. Nothing happens.
If it is not a problem, put the NSLog in:
-(void) mainMenuViewLibrary:(MainMenuView*)controller withString:(NSString*)string;
in your WorkspaceView and check whether the method is called.
Hope that helps.