Circular references in Swift / Objective-C bridging
Any iOS/MacOS project with more than two years of history has to deal with the Swift and Objective-C mix and match, where different languages try to coexist in a peaceful ecosystem.
This peace may however be threatened by some special conditions where the bridge between the two languages cannot be built correctly due to ambiguity, like in the case of circular references.
Take for instance this scenario:
- The ObjcMainClass is composed by an object that conforms to the ComponentProtocol written in Swift, therefore it needs to import the ProjectName-Swift.h generated file
- The ObjcMainClassTests is the Objective-C unit test class that will test the ObjcMainClass component. It will make use of a StubComponent object that will conform to ComponentProtocol in order to control the execution flow of the public methods to test.
- StubComponent is defined in the unit test target, and it will need to include the @testable import ProjectName code in order to find the protocol definition.
- Since StubComponent lives in the unit target and it’s written in Swift, the ObjcMainClassTests has to import the ProjectNameTests-Swift.h generated file.
Try to run unit tests and what you get is the “Module ProjectName not found in ProjectNameTests-Swift.h” error:
This scenario might be quite common in projects that embrace a mockist unit testing approach where concrete stubs for new Swift interfaces are passed to existing Objective-C entities.
The best solution is to reduce the number of bridges needed between your Objective-C and Swift code. Try to migrate the module or your object graph to Swift, or temporarily write the protocol and stubs in Objective-C before planning the migration itself.
This second approach is showed in the demo project available on Github, where you can find both failing and passing scenarios described here: https://github.com/matsoftware/TestObjcSwiftCircularReference .