iOS Tests working with Objective-C and Swift class together

MonGuNare
If let swift = Programming!
5 min readJan 26, 2017

Introduction

Swift is awesome. Yes I know. But still many companies are using Objective-C and starting to add new Swift code in it. Our generous Xcode tool supports to combine these two different style codes. It is only needed a small, no frankly, complicated setting.
The situation is still fine when my concern is only main target project. However once I tried to use existing Objective-C class inside of Swift test class, I stuck in the middle.
Here is solutions that how to use Objective-C and Swift class together for each situations.

Four different situations using Objective-C and Swift class

  • using Objective-C class in Objective-C unit test class
    This is common case that you can simply import objc class in a unit test class. Easy peasy!
#import <XCTest/XCTest.h>
#import “ObjcClass.h”
@implementation SampleProjectForTestingTests- (void)testExample {
ObjcClass *aClass = [ObjcClass new];
[aClass doSomething];
}
@end
  • using Swift class in Swift unit test class
    There are two ways that you can use a Swift class in your Swift test class. First way is, select Swift class file and set “Target Membership” for Tests.

But think though. What if you have a bunch of files needed to be added? It won’t be compiled not only adding all classes directly used in in the test class but also classes that are imported by that class.

Easier, and more effective way is using “testable” attribute.
The definition of “testable” attribute in Apple document is like that;

testable
Apply this attribute to import declarations for modules compiled with testing enabled to access any entities marked with the internal access-level modifier as if they were declared with the public access-level modifier. Tests can also access classes and class members that are marked with the internal or public access-level modifier as if they were declared with the open access-level modifier.

Now, code will looks like below,


import XCTest
@testable import SampleProjectForTesting
class SampleProjectForTestingTests: XCTestCase {
func testExample() {
let aClass: SwiftClass = SwiftClass()
aClass.doSomething()
}
}
  • using Objective-C class in Swift unit test class
    You may find a header file, “YourProject-Bridging-header.h” in you project hierarchy. It is automatically generated by Xcode whenever you create Swift class file in Objc project or vise versa.

    Open “YourProject-Bridging-header.h” and import any class that you want to handle in Swift class.
//
// Use this file to import your target’s public headers that you would like to expose to Swift.
//
#import “ObjcClass.h”

Once you register classes, Xcode will convert those classes internally when Xcode build time. Dark magic!!!

Now, without specifying any key words, you can use those Objc classes in Swift class in your project and test file.


import XCTest
@testable import SampleProjectForTesting
class SampleProjectForTestingTests: XCTestCase {
func testExample() {
let aClass: SwiftClass = SwiftClass()
aClass.doSomething()

let aObjcClass: ObjcClass = ObjcClass()
aObjcClass.doSomething()
}
}
  • using Swift class in Objective-C unit test file
    Objective-C class is converted to Swift class by Xcode’s dark magic, but how about the opposite way? In fact, Swift class through using “objc” keyword and inheriting “NSObject” is also converted to Objc class by our mighty Xcode builder. The Swift class looks like that;
import Foundation
@objc class SwiftClass: NSObject {
func doSomething() {

}
}

And hidden Objc header file which contains all Swift classes definition in it is also created at the build time so that the other Objc class can use Swift class. The header file’s name is “YourProject-Swift.h” which is invisible but still can be accessed manually.

Now, you got a clue. Through importing this header file, you can access Swift class in Objc class. It is perfectly work within your project but once you try to apply same process to unit test file, boom! you will meet an error like that;

The reason why your test class couldn’t find the Swift classes is that the header file is an auto-generated file which only existed in the build directory, and the test target doesn’t see the main target as a framework module to be imported.

To solve this frustrated issue, adding the folder containing the main target’s auto-generated header file to the test target’s header search paths. Go to test target > Build Settings > Header Search Paths, and add $CONFIGURATION_TEMP_DIR/YourProject.build/DerivedSources in it.

Go back to your test class, do clean up and build again. Now the red alert has gone and you can access Swift class freely!

Conclusion

Connecting Objective-C and Swift class in unit test still uncomfortable that developer has to set a bunch of settings and follow certain processes. I strongly believe that Xcode will support those chore works in some later version so that it will be linked each other clicking just one-button option. :)

Here is reference source code;
https://github.com/ahimahas/TestsBetweenObjcAndSwift

--

--