Swift protocol in Objective-C category - objective-c

I have a protocol defined in Swift:
#objc public protocol UploadProtocol: class {
func isUploaded() -> Bool
...
}
I'm importing my ProjectName-Swift.h file in my ProjectName_Prefix.pch file (old project).
In Objective-C I have a category on a class which implements the protocol. I'm trying to figure out how to let the compiler know about that implementation. This is what I have been doing:
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#implementation Person (Upload)
...
#end
This gives me:
Cannot find protocol definition for 'UploadProtocol'
I can only think of three other ways of setting this up and all of them have problems. What am I missing?
First Way (Apple Recommended)
In Using Swift with Cocoa and Objective-C (Swift 2.2), Apple says, "An Objective-C class can adopt a Swift protocol in its implementation (.m) file by importing the Xcode-generated header for Swift code and using a class extension." But their example is for a standard class. If I try to do this with my category:
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person (Upload) <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
This results in:
Duplicate definition of category 'Upload' on interface 'Person'
Second Way
Ok, so I want to avoid defining the upload class twice. So I'll remove it from the .h.
// Person+Upload.h
#import "Person.h"
#interface Person ()
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person (Upload) <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
In this case I get:
Category is implementing a method which will also be implemented by its primary class
I'm currently declaring the method in the .h so that other classes can call some of those methods without casting to the protocol to access that method.
Third Way
If I leave the category name in the .h but take it off in the .m
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person () <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
Then I get:
Category is implementing a method which will also be implemented by its primary class
Is the only solution to cast it to the protocol every time? Adding this type of line in multiple places seems like a code smell:
Person <Upload> *s = (Person <Upload> *)self;
Any other solutions?

Thanks for the interesting question!
Your initial approach will actually work with an addition of a forward declaration of the protocol:
// Person+Upload.h
#import "Person.h"
#protocol UploadProtocol; // NOTE THIS!!!
#interface Person (Upload) <UploadProtocol>
//- (BOOL)isUploaded; // This is not really needed
#end
// Person+Upload.m
#import "Person+Upload.h"
#import "...-Swift.h" // IMPORTANT!!!
#implementation Person (Upload)
// HERE you implement isUploaded, of course.
...
#end
This will still cause a compiler warning Cannot find protocol definition for 'UploadProtocol', but it should work.
The Apple-recommended way also works, the Duplicate definition of category is just a warning, but Person won't be recognized as conforming to the protocol by Swift code, even though its conformance will be recognized by Objective-C. The initial approach will be fine in Swift, too.

Related

Objective-C: can initialize one class but not the other

I have some objective-C code with a few classes. I'm trying to initialize each of the first two classes within the third class. One works fine, turns the right color in Xcode and allows me to jump to the definition. The other for some reason doesn't. I've poured over it but can't see what distinguishes the two.
MainClass.m:
#import "Class1.h"
#import "Class2.h"
#implementation MainClass
Class1 *class1 = [Class1 new];
Class2 *class2 = [Class2 new];
#end
Class1.h:
#ifndef Class1_h
#define Class1_h
#interface Class1 : NSObject
#end
#endif
Class2.h:
#ifndef Class2_h
#define Class2_h
#interface Class2 : NSObject
#end
#endif
There's no difference in header files of Class1.h and Class2.h. Both are imported into the MainClass.m But for some reason Class2 isn't recognized in MainClass.m, yields "Initializer element is not a compile-time constant" on Build+Run and doesn't allow me to command-click and jump to the definition.
I'm not sure where to look at this point. I've copied and pasted the code from the Class1.m file into Class2.m only changing the #import Class2.h header import and #implementation Class2 lines to make sure its not something in the .m files...problem persists.
You have an apostrophe instead of a double-quote at the end of your second import.
You might have a lingering #define of Class2_h somewhere. (This can happen if you use this #ifndef/#define/#endif pattern, especially if cutting and pasting code.)
For what it’s worth, you really don't need this #ifndef/#define/#endif pattern around your #interface references. Often C programmers are used to using this pattern a lot, but it’s just not needed in Objective-C class definitions.
For example, consider scenario where Class1 had a property that was a Class2:
// Class1.h
#import "Foundation/Foundation.h"
#import "Class2.h"
#interface Class1 : NSObject
#property (nonatomic, strong) Class2 *class2;
#end
And
// Class2.h
#import "Foundation/Foundation.h"
#interface Class2 : NSObject
#end
Then MainClass can import both headers without incident (even though Class1.h currently imports Class2.h already):
// MainClass.m
#import "MainClass.h"
#import "Class1.h"
#import "Class2.h"
#implementation MainClass
- (void)foo {
Class1 *class1 = [Class1 new];
Class2 *class2 = [Class2 new];
NSLog(#"Class1: %#", class1);
NSLog(#"Class2: %#", class2);
}
#end
You would have thought that this redundant #import "Class2.h" would cause problems, but it doesn’t. Objective-C tried to get us out of worrying about all of these #define references.
Now, that having been said, using that above example, Class1.h might not even #import the Class2.h file, at all, but it might just do a forward declaration of the class. And if Class1 implementation needed to interact with Class2 objects, you’d do the #import of Class2.h in Class1.m, not Class1.h:
// Class1.h
#import "Foundation/Foundation.h"
#class Class2;
#interface Class1 : NSObject
#property (nonatomic, strong) Class2 *class2;
#end
But regardless, we just don't tend to use the #ifndef/#define/#endif pattern in Objective-C.

Duplicate protocol definition warning

First, I have seen this question as well as this, but my problem is not addressed there.
I have a protocol ProtocolA defined in its own header file. Then I have two classes ClassA and ClassB which both conform to this protocol so the protocol-header is imported in their header files.
Now it gets a bit complicated. ClassA is used (and thus imported) in a third ClassC. This class conforms to a second protocol ProtocolB. This protocol also has its own header file where it uses and imports ClassB. So my ClassC imports (either directly or indirectly) both ClassA and ClassB (which both import ProtocolA). This gives me the following warning regarding ProtocolA:
warning: duplicate protocol definition of '…' is ignored
Why is this happening? It was my understanding that the #import macro was invented exactly for avoiding this kind of problems which we had with #include. How can I solve the issue without using an include guard? I can't really remove any of the imports.
EDIT: here is the code to illustrate the situation:
ProtocolA.h
#protocol ProtocolA <NSObject>
- (void)someMethod;
#end
ClassA.h
#import "ProtocolA.h"
#interface ClassA : NSObject <ProtocolA>
...
#end
ClassB.h
#import "ProtocolA.h"
#interface ClassB : NSObject <ProtocolA>
typedef enum Type {
TypeB1,
TypeB2
} TypeB;
...
#end
ProtocolB.h
#import "ClassB.h"
#protocol ProtocolB <NSObject>
- (TypeB)someMethod;
#end
ClassC.h
#import "ProtocolB.h"
#interface ClassC : NSObject <ProtocolB>
...
#end
ClassC.m
#import "ClassC.h"
#import "ClassA.h" // the warning appears here
#implementation ClassC
...
#end
do not Import ClassB in ProtocolB header, just use #class ClassB; in it and remove #import "ClassB"

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

Move Delegates to their own classes?

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.

#protocol implementation in #interface in Objective-C

I need to develop an application which has a interface which implements methods of 3 protocols.
Assume protocol A extends protocol B and protocol C, and interface implements protocol A.
This is how my code looks,
// This is in MyClass.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "protocol_A"
#interface MyClass : NSObject <protocol_A>
{
}
#end
//This is MyClass.m file
#import "MyClass.h"
#implementation myClass
-(void)methodinA
{
NSLog(#"I'm in protocol_A");
}
}
-(void)methodinB
{
NSLog(#"I'm in protocol_B");
}
-(void)methodinC
{
NSLog(#"I'm in protocol_C");
}
#end
//This is protocol_A.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "protocol_B.h"
#import "protocol_C.h"
#protocol protocol_A <protocol_B, protocol_C>
-(void)methodinA;
#end
//This is in protocol_B.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol protocol_B
-(void)methodinB;
#end
//This is in protocol_C.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol protocol_C
-(void)methodinC;
#end
i'm getting an exception , and my app is getting crashed...
***Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyClass 0X323nm31>setvalue:forundefinedKey:]:this class is not key value coding-compilant for the key window'.
Plz Tel me how to solve this problem??
So where you're getting this from (and the reason you're getting it 3 times) is you've got a mistake in your protocol definitions. You have:
//This is in protocol_C.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol protocol_C
{
}
-(void)methodinC;
#end
You can't declare class members in a protocol: only methods. Because of this, you don't need (and, as you've discovered) can't have the curly braces in the protocol definition. As such, you need this for your protocol definitions:
//This is in protocol_C.h file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol protocol_C
-(void)methodinC;
#end
Removing those should solve your issue.
When making new files, I always go through Xcode's new-class-files process, as it frequently gives you lots of convenient stuff. Here is the contents of a new protocol_D declaration fresh from Xcode:
#import <Cocoa/Cocoa.h>
#protocol protocol_D
#end
Hope this helps!
TL;DR: Protocol definitions can't have curly-braces anywhere in them.
Protocols generally go in a .h file; always go in a .h file if you plan on using them anywhere.
Just like everything else, you need to #import the .h file that contains the definition of the protocol before you use it.
So, in MyClass.h (it really should be capitalized -- Classes are always capitalized in Objective-C), #import the various protocol .h files.
Your protocol_A.h file declares conformance to protocol_B and protocol_C, yet you haven't imported the headers for protocol_B and protocol_C. This means that you are declaring conformance to protocols that as far as the compiler is concerned, don't exist in protocol_A.h. You need to import the headers:
In protocol_A.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "protocol_B.h" //note these new imports
#import "protocol_C.h"
#protocol protocol_A <protocol_B, protocol_C>
-(void)methodinA;
#end
Also see Apple's Communicating with Objects, which discusses delegates, protocols, and selectors. Though its listed under Mac OS X, most (if not all) appears to apply to iOS also.