How to prevent circular reference when Swift bridging header imports a file that imports Hopscotch-Swift.h itself - objective-c

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

Related

Objective C interface generated header can't be imported in other header

I am not an Obj C pro, but I should write some code in Objective C and bridge a Swift code to it.
I success in importing the Generated Header to the .m file:
#import "<my_module>-Swift.h"
But when I try importing the same header to the .h file it throws this error:
BTW, I only want to add a public variable that instances from a Swift class to a specific obj c class. I've tried to put these lines at the .h and .m files:
#property (nonatomic, readwrite, strong) Card *card;
What should I do?
In your case, since all you need to do is declare a property of type Card*, you don't actually need to import the header—you can just forward-declare the class with #class Card; before using it.
If you want to reference a Swift class in an Objective-C header, you cannot #import the *-Swift.h file, but should rather use a forward declaration as described in https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html, see the section titled Referencing a Swift Class or Protocol in an Objective-C Header. This is basically what #jtbandes is suggesting in the comment.
One gotcha here: the Swift class you want to use in Objective-C must extend the NSObject class, directly or indirectly.

Objective-C protocol not recognized in Swift

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.

Auto-generated "ModuleName-Swift.h" header not including Swift classes

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

what does #class do in iOS 4 development?

Is there any difference in doing
#class MyViewController;
rather than doing the normal import of the .h into the appdelegate.h
#import "MyViewController.h"
I've seen some example recently that use the #class way and wondered if there any differences.
thanks.
There is a big difference.
#class MyViewController;
Is a forward declaration for the object MyViewController. It is used when you just need to tell the compiler about an object type but have no need to include the header file.
If however you need to create an object of this type and invoke methods on it, you will need to:
#import "MyViewController.h"
But normally this is done in the .m file.
An additional use of forward declarations is when you define a #protocol in the same header file as an object that uses it.
#protocol MyProtocolDelegate; //forward declaration
#interface MyObject {
id<MyProtocolDelegate> delegate;
...
}
...
#end
#protocol MyProtocolDelegate
... //protocol definition
#end
In the above example the compiler needs to know that the #protocol MyProtocolDelegate is valid before it can compile the MyObject object.
Simply moving the protocol definition above MyObject definition would also work.
#class allows you to declare that a symbol is an Objective-c class name without the need to #import the header file that defines the class.
You would use this where you only need the class name defined for the purposes of declaring a pointer to the class or a method parameter of the class, and you do not need to access any methods, fields, or properties in the class.
It saves a minuscule amount of compile time vs the #import, and it sometimes helps avoid messy include circularity issues.
[And, as rjstelling points out, it's sometimes useful where you have interleaved declarations and you need to "forward declare" something.]

How to declare protocol in separate header file

I have two classes. Both these classes are delegates of each other. This gives me error like "Can not find protocol declaration". After searching on net, I came to the conclusion that, this is the case of cyclic dependency.
To break this dependency the solution they have suggested is to define protocol in another header file. I could not find any tutorial on how to do this and how will it affect my code?
I have a example for you..
#class ClassA;
#class ClassAController;
#protocol CreateClassADelegate
-(void)CreateClassA:(ClassAController *)sender didCreateClassA:(ClassA *)ClassAObj;
-(void)CreateClassACancel:(TSInputController *)sender;
#end
Check #Toro's answer in this previous SO question
UIViewController calling each other's delegate
In case you are using XCode 4 you just creating new file as always, the difference is that you need to choose Objective-C protocol in Cocoa Touch section rather then Objective-C class or UIViewController subclass.
Other approach you may use is to create new Objective-C class and then just delete the .m file manualy and change #interface to #protocol in .h file.