37

I'm finding it very hard to find out how to configure test targets in Xcode 6b4. Can someone point me in the right direction given this scenario.

I have a mostly Swift project. However, there are some 3rd party Objective-C dependencies, which get put into the application's bridging header. I want to write tests for my Swift code. Ideally, in Swift. The problem I have is this....

  1. If I create a Swift test case, then the compiler complains that it can't find the Objective-C headers in the application's bridging header.

  2. If I create an Objective-C test case, then I cannot import the Swift classes which I want to test.

The only thing which I can do is write Objective-C tests cases, which don't touch any Swift. I cannot write "purely Swift code/tests" due to the Objective-C dependencies.

Does anyone have any advice or had success on this. Or is this the current state of things in Beta 4?

Daniel Thorpe
  • 3,911
  • 2
  • 28
  • 28

8 Answers8

29

I am not sure if you are still looking for this as Xcode 6 is not in beta any more, but you can add your Objective-C Bridging Header file to your Unit Test Target in the target settings. This fixed the issue for me.

Objective-C Bridging Header Xcode settings

pkamb
  • 33,281
  • 23
  • 160
  • 191
Barlow Tucker
  • 6,229
  • 3
  • 36
  • 42
  • 4
    Did this make the `$(PRODUCT_MODULE_NAME)-Swift.h` file available in your Objective-C tests? Doesn't work for me... – yonix Dec 01 '14 at 13:43
  • 2
    @yonix this won't make $(PRODUCT_MODULE_NAME)-Swift.h available, this is for the other way around, i.e. using objective c code in Swift – Joshua Book Jan 23 '15 at 23:45
  • @Barlow Tucker, I was banging my head for the better part of 2 hours, this was the fix thank you!!! – xBACP Nov 20 '15 at 04:03
  • If PRODUCT_MODULE_NAME has any nonalphanumeric characters, such as a period (.), they are replaced with an underscore (_) in your product module name and you cannot override the product module name of a framework. – kaushal Apr 26 '18 at 06:40
23

From Xcode 6 Beta 4 release notes known issues:

Testing

• Unit tests written in Objective-C cannot currently import the Swift generated interfaces header ("$(PRODUCT_MODULE_NAME)-Swift.h") for application targets, and therefore cannot be used to test code that requires this header. (16931027)

• Workaround: Unit tests for Swift code should be written in Swift. Unit tests written in Objective-C for framework targets can access the Swift generated interfaces by importing the framework module using '@import FrameworkName;’.

So, you can't currently test Swift code in Objective-C.

Also from the release notes:

A limitation of the access control system is that unit tests cannot interact with the classes and methods in an application unless they are marked public. This is because the unit test target is not part of the application module.

Chris Lattner has stated on this post on the Developer Forums that they are still evaluating the situation to see how they can conciliate unit tests and the newly implemented access controls.

So, I'd expect many changes to unit testing both in Swift by itself, as well as in projects that use both Swift and Objective-C.

Cezar
  • 55,636
  • 19
  • 86
  • 87
  • 4
    Yeah, I've read all of that. So, the question is, if I have Swift classes which I want to test, and Swift test cases, how do I make the test target compile when the application Swift classes require Objective-C imports from the bridging header... – Daniel Thorpe Jul 24 '14 at 16:44
  • 1
    @DanielThorpe - I agree, this answer doesn't really address your question. I too would like to write Swift unit tests against Swift code that also uses ObjC frameworks. – ColinE Nov 06 '14 at 09:51
  • @ColinE - yeah, I've sort of managed to avoid the issue by creating another Objective-C framework which includes those Objective-C dependencies, unfortunately using submodules, which I them import into my Swift framework. – Daniel Thorpe Nov 06 '14 at 23:33
  • 1
    perhaps update this answer since it can be done in the latest version? – Daniel Galasko Nov 13 '14 at 10:15
  • 2
    can anyone confirm that this is still an issue or that it has in fact been updated? I am using xcode 6.1.1(6A2008a) and tests will not compile as they cannot find the $(PRODUCT_MODULE_NAME)-Swift.h file. Actually scanned through the derived data directory and found the file and explicitly imported that and still the file cannot be find when built for tests. – Joshua Book Jan 23 '15 at 23:48
8

We made this working, i saw all the possible errors along the way: module name errors when getting a VC from storyboard, MyType_MyType_ errors specific to coredata entities, and so on but this were the worst.

The trick is to use the -Swift.h file from the build target instead the tests target, because in the tests target sometimes you have to include objc files but not swift files and objc will not recognise those if they are linked together, but in the build target everything is there.

  • First thing make sure the -Swift.h of the tests target is not named the same as the build target.

  • Include in the tests .pch or in each individual file the -Swift from debug target.

  • Let the compiler know where is that file (in derivedsources) by editing the User Header Search Paths with: $(OBJROOT)/MyProjectName.build/Debug-iphonesimulator/MyProjectTarget.build/DerivedSources If not sure just go inFinder and find the names. If there are spaces in the names you need to add \ before space

  • You can find more about the project variables here: How do I print a list of "Build Settings" in Xcode project?

  • Always search User Paths should be set to YES

  • Don't include the swift files in the tests target, but use @testable

I hope Apple did fix this in the next xcode.

Community
  • 1
  • 1
Cristi Băluță
  • 1,295
  • 15
  • 27
  • I made the subtle mistake of checking my swift class's Target Membership for both the main target *and* the Tests target. This linked two versions of the same class into my test, causing some equality tests to fail miserably. The fix was to modify my Tests target's `Header Search Paths` as suggested to pull the autogen'd `*-Swift.h` header from the main target, and uncheck the test Target Membership for my swift file. – qix Jul 04 '17 at 05:09
  • I also found this article to be a nice overview of the various permutations: https://medium.com/@ahimahas12/ios-tests-working-with-objective-c-and-swift-class-together-aaf40f91a27c – qix Jul 04 '17 at 05:09
6

For Xcode 10 to have the swift classes in your Obj-C test you will need to add the auto-generated header Module-Swift.h in the user search path of your test target.

Go to test target > Build Settings > Header Search Paths, and add $CONFIGURATION_TEMP_DIR/YourProject.build/DerivedSources in it

For additional reference please look iOS Tests working with Objective-C and Swift class together

Mike.R
  • 2,824
  • 2
  • 26
  • 34
5

I made working by updating "Objective-c generated Interface Header Name" to "YourApplication-Swift.h"

By default settings for this is to generate TargetName-Swift.h, compiler never generate YourApplicaitonTest-Swift.h

Jignesh Sheth
  • 131
  • 1
  • 2
  • 4
  • Thanks! You're a lifesaver. In one of my projects I didn't have this in build settings (probably created with old Xcode) so I changed the PRODUCT_MODULE_NAME instead to be without 'Tests'. – yonix May 29 '15 at 09:41
  • 1
    Wrote a blogpost about this: http://codesheriff.blogspot.co.il/2015/05/importing-swift-code-from-objective-c.html – yonix May 29 '15 at 10:53
5

I came across this question when trying to figure out how to access my project's Swift code from my Swift tests. Both my project and tests were originally started in Objective-C so they have all the bridging headers setup.

In order to access the Swift classes from my project I had to add:

@testable import MyProjectName

To the top of my Swift test file. After building it for the tests then Xcode recognized my swift code.

I hope that helps someone else.

RyanJM
  • 7,028
  • 8
  • 60
  • 90
2

I had this problem and tried to solve it in build settings for the test target. By changing search paths, importing swift header, importing things in the bridging header. However it turned out to be in my podfile. I had to add a link_with with the test target. Like this:

platform :ios, '8.0'
link_with 'YourApp', 'YourAppTests'
pod 'ThePopularPod', '1.2.3', :inhibit_warnings => true
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • [!] Invalid `Podfile` file: [!] The specification of `link_with` in the Podfile is now unsupported, please use target blocks instead.. – Olcay Ertaş Jul 13 '18 at 14:14
0

Try putting the #import "xyz-Swift.h" statements into your precompiled header files. That way you have different import statements for all of your Obj-C headers, depending on the target:

MyProject-Prefix.pch (app target):

#import "MyProject-Swift.h"

MyProjectTests-Prefix.pch (test target):

#import "MyProjectTests-Swift.h" 
Goodsquirrel
  • 1,476
  • 1
  • 15
  • 29
  • Could someone give me a hint, why this solution is bad (and gets downvoted)? It solved the problem for me. I see that now I'm importing the file unnecessarily in Obj-C headers that don't need it, but is that such a horrible thing? Or is there another reason? – Goodsquirrel Nov 11 '15 at 16:09