I am working in an app written in Objective-C and we would like to begin integrating some Swift into it.
I have written a simple view controller class in Swift and to work in the app it must conform to a protocol that was written in Objective-C. In my Swift class I have the following declaration:
#objc class SwfViewController: UIViewController, theProtocolName {
//some code
}
In my bridging header file I have the class name of the protocol referenced.
#import "theProtocolName.h"
I have implemented all of the required methods listed in the protocol, yet still I get an error saying
SwfViewController does not conform to protocol 'theProtocolName'
I'm fairly new to Swift and could have easily left out something. Any suggestions of what to check? Thanks!
You should take into account that Swift signature for methods is slightly different from objective-c. For example:
A protocol like this:
#protocol MyProtocol <NSObject>
- (void)didFinish:(MyClass *)class withError:(id)errorMessage;
#end
A class that conforms to this protocol in Swift should be:
#objc class SwfViewController: UIViewController, MyProtocol {
func didFinish(whatEveryouWant1: UIViewController!, withError whatEverYouWant2: AnyObject!){
}
}
Please note id becomes AnyObject and Obj-C references are translated with ! as implicitly unwrapped optionals.
Generally this error means that your class is missing required methods which is the part of the point of the protocol. The class seems to realize that it need to follow the protocol you've told it about, but now need to define the required pieces.
You should be able to expand the error message by clicking on it to find out which pieces are missing.
Related
In Java, C++11 and some other languages you can specify that a method is intended to override another method from a base class or interface, if you then at a later point remove the method from the base class you get a compiler error. I use protocols with optional methods a lot and find that if I remove a method from there I have a tendency to forget to remove the code that implemented the method. This does not generate an error or a warning, thus creating a "dead" method.
Consider:
#protocol Prot <NSObject>
#optional
- (void)bar;
- (void)tempBar;
#end
#interface MyType : NSObject <Prot>
#end
#implementation MyType
- (void)bar { /**/ }
- (void)tempBar { /**/ }
#end
If I at one point remove tempBar from the protocol, I would like to get at least a warning from tempBar being implemented in MyType.
Is there any way in Objective-C to specify that a method is expected to be an implementation of a protocol method?
Objective-C is a dynamic language and this is rather impossible to enforce at compile time. Note that in Obj-C you can actually call methods there are not even there and the app won't crash (well, the default implementation will raise an exception but you can change that behavior).
The method can be also added in an extension, or added at runtime. Or it is just not present in the header.
Note there is also the opposite problem. When subclassing, you can override a method which you don't even know is there, because it is not present in the headers.
This is one of the reasons why Apple is moving to a more predictable language, that is, Swift.
The following paragraph from the section on Interoperability of the Using Swift with Cocoa and Objective-C (Swift 2.1) documentation seems to suggest that there is a way to use a Swift class that does not inherit from an Objective-C class for interoperability.
When you create a Swift class that descends from an Objective-C class, the class and its members—properties, methods, subscripts, and initializers that are compatible with Objective-C—are automatically available from Objective-C. In some cases, you need finer grained control over how your Swift API is exposed to Objective-C. You can use the #objc attribute if your Swift class doesn’t inherit from an Objective-C class, or if you want to change the name of a symbol in your interface as it’s exposed to Objective-C code.
And I attempted the following:
import Foundation
#objc class FooBar {
#objc var name: String;
init() {
name = "Hello World!"
}
}
But unfortunately, it gives a compilation error: Only classes that inherit from NSObject can be declared #objc
Am I misinterpreting something?
Take a look at this thread on the Apple Developer Forums.
Basically what you say was possible in Xcode <=6, but removed on Xcode 7
Pretty much yes. #objc on Swift-rooted classes never quite behaved like an NSObject-rooted class, leading to various weirdness in the generated header and at runtime. You can still treat any Swift class instance as an AnyObject, mark methods and properties on a Swift class as #objc, and conform to Objective-C protocols; the class just isn't exposed in the generated header and doesn't default to having its members available in Objective-C.
I am trying to reference some Swift-defined classes from my Objective-C implementation file, but for some reason, though I've gotten the header file to auto-generate, it doesn't appear to be including any information about the Swift classes in the project.
My Swift class is attributed with #objc yet even after importing the "-Swift.h" file, I still get a "Use of undeclared identifier" error when compiling.
I can't figure out what I'm missing. I have Defines Modules set to YES in the project.
Also of note: if I command-click the symbol from my Obj-C file, Xcode successfully finds the definition in the Swift file.
Make sure in build setting you have got this setup:
Objective-C Bridging Header : $(SRCROOT)/Sources/SwiftBridging.h
Sometime when you import a swift file directly Xcode don't prompt you to add a bridging header. it's a must have step even you don't call objective-c from swift.
You may need to derive from NSObject.
I encountered a similar situation where some Swift classes were not being exposed to Obj-C through the auto generated header. The solution is to derive Swift classes from NSObject.
class SwiftClassNotInHeader { }
class SwiftClassInHeader : NSObject { }
From MyApp-Swift.h
SWIFT_CLASS("_TtC6Server18SwiftClassInHeader")
#interface SwiftClassInHeader : NSObject
- (SWIFT_NULLABILITY(nonnull) instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
SwiftClassNotInHeader is not in myApp-Swift.h
I am integrating Swift into a large existing Objective C project and have run into what I think is a circular reference.
The classes in question are as follows:
Objective C Controller
#import "Hopscotch-Swift.h"
#interface MyController : UIViewController<MyProtocol>
...
#end
Swift Protocol
#objc protocol MyProtocol: NSObjectProtocol {
...
}
Bridging Header
#import "MyController.h"
This code fails to compile because the Hopscotch-Swift.h file will not generate.
I think this is due to a circular reference error as I can import Hopscotch-Swift.h into objective c headers that are not included in Hopscotch-Bridging-Header.h and it works fine.
Is there a workaround for this issue or should I file a radar with Apple?
Forward declaration should work, in your case.
In your .h:
#protocol MyProtocol;
#interface MyController : UIViewController<MyProtocol>
#end
In your .m:
#import "HopScotch-Swift.h"
From How can I add forward class references used in the -Swift.h header? and the Swift interoperability guide:
If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.
I ran into this when trying to use Swift classes inside Objective-C protocols, where the protocol was also implemented by another Swift class. It reeked of circular references and I guessed that it might be a problem trying to circularly generate the bridging headers, rather than a 'normal' circular include problem.
The solution, for me, was to just use forward declarations before the protocol declaration:-
// don't include the MyProject-Swift.h header
// forward declaration of Swift classes used
#class SwiftClass;
#protocol MyProtocol <NSObject>
- (SwiftClass *)swiftClass;
#end
The forward declaration by itself didn't work for me. It compiled without errors but still had warnings that the protocol couldn't be found. I treat all warnings as errors, so this isn't good enough.
I was able to fix it by moving the protocol implementation into another category header.
So here's what worked for me:
In my MyOtherSwiftFile.swift:
#objc protocol MyProtocol: class {
func viewController(didFinishEditing viewController: MyViewController)
}
In my MyViewController.h:
#interface MyViewController // Removed protocol implementation declaration here
#end
Added MyViewController+MyProtocol.h to project, and put this in there:
#interface MyViewController (MyProtocol) <MyProtocol>
#end
The methods themselves can stay where they are if you want.
After you implement the above and compile, you'll get compiler warning(s) somewhere in your code that requires that MyViewController implements MyProtocol. In that file, you will #import "MyViewController+MyProtocol.h"
Alternatively you can convert your protocol to an Objective-C protocol MyProtocol.h and then use it in Swift by including MyProtocol.h in your bridging header.
You could something like this in the .h file you suspect to trigger the circular reference:
#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif
For every object that can have a delegate, there is a corresponding protocol, that declares the messages that the object can send it's delegates. The delegate implements methods from the protocol for events it is interested in.
How can one view the protocol in order to find out what functionality needs to be implemented?
Protocols in Objective-C are non-essential, but they are useful; Protocols are usually declared in header (.h) files:
#protocol MyAwesomeProtocol
-(void)thisMethodIsRequired;
#optional
-(void)theseMethodsAreOptional;
#end
... and are usually used in a couple of places:
1: In an instance variable declaration:
#class Foo : Bar
{
id<MyAwesomeProtocol> someIvar;
}
#end
2: In property declarations:
#class Foo : Bar
{ }
#property (assign) id<MyAwesomeProtocol> someProperty;
#end
3: In code (Try to avoid this, but it's legal):
if(...)
{
[(id<MyAwesomeProtocol>)obj foo];
}
If you're using Xcode, you can always command-click a protocol that appears anywhere in your code to jump to the header where that protocol is defined. This is true even of Apple's protocols, since header files are not compiled. Also, the documentation available through Xcode provides additional insight on what methods are required or optional.
Since you can define optional protocol methods, you should always check to see if your delegate -respondsToSelector:#selector(isThisMethodImplemented:), since the language doesn't do this for you.
Also, if you're using Xcode, you can option-click a class in your code to bring up the quick documentation panel, which has an option to go to the full documentation for the class of the object you clicked on.
You can either look at the documentation or view the corresponding header file by Command-clicking the protocol in Xcode (Command-doubleclick in Xcode 3).
Check out the doc the delegate property, it is almost all the time defined is id type and which protocol it is conforming to : id <TheProtocolYouLookFor>.
If not, read down the description and you will find more information about the protocol. Protocol names are also links in general.