How do I import a Swift file from another Swift file?
Asked Answered
N

13

164

I simply want to include my Swift class from another file, like its test

PrimeNumberModel.swift

import Foundation

class PrimeNumberModel { }

PrimeNumberModelTests.swift

import XCTest
import PrimeNumberModel  // gives me "No such module 'PrimeNumberModel'"

class PrimeNumberModelTests: XCTestCase {
    let testObject = PrimeNumberModel()  // "Use of unresolved identifier 'PrimeNumberModel'"    
}

Both swift files are in the same directory.

Nape answered 4/6, 2014 at 5:35 Comment(2)
According to apple docs you don't need an import when both files have same target. Sadly tests have a different target. One possible solution might be to do an import statement using yourModule/PrimeNumberModel.Tatianna
@Nape I am facing same issue. How it will be resolved? I am stuck now.Palacio
B
135

I had the same problem, also in my XCTestCase files, but not in the regular project files.

To get rid of the:

Use of unresolved identifier 'PrimeNumberModel'

I needed to import the base module in the test file. In my case, my target is called 'myproject' and I added import myproject and the class was recognised.

Brian answered 4/6, 2014 at 18:56 Comment(19)
Yep, this is the way to do it. Except I think it's not really your project that you're importing, but rather the target containing the code that you want to test?Gesso
Thanks man, this saved me a lot of time. You need to import <TARGET>.Mather
This needs more upvotes. The answers advising to just add the .swift file to the test target are not exactly wrong, but it's not the way it should be doneFromm
But what to do with my project name "Wildlife League", which has space in it?Consols
Starting with Beta 4, you'll also need to make sure your access control settings are correct. You'll need to explicity set both the class you're testing and the functions you're testing within that class as public. Otherwise the XCTestCase subclass won't be able to "see" what you're trying to test. I wasted a few hours on this last night :)Nigrosine
I'm experiencing the same issue as OP, but the import didn't help. My target is called PresentAPIClient, but the only things listed in the module are import Foundation and import UIKit, unlike example projects, which seem to list the available interfaces for the module. All of my source files are included in the target under compile sources.Shaman
@AllenLin: Good question. I had the same problem and solved it by just renaming my project. I did that by clicking on my projects top most item in the project navigator, pressing enter and removing any whitespaces. XCode will automatically handle renaming related files.Distinctly
I had to manually change the "Test Host" entry in Build Settings of my Test target though. Check that also if you get any Mach-O Linker errors after renaming the project.Distinctly
If I'm not mistaken, the Product Module Name (in target build settings) isn't always the same as the target's name and it's the module's name that should be used in the import statementZachariahzacharias
I have remove the space in the name of my project but still it is showing the error No Such Module. Why?Palacio
I think the best practice would be to add the model swift file and any other files to the test target. Then you don't need to play games with the access modifiers and expose everything that you need to test.Ivanovo
On top of doing this, I had to set the Target Membership in the File Inspector in the right sidebar of my swift file.Elspet
If a Swift target has a space in its name, you can import it by replacing the spaces with underscores in the import statement. "My Swift Class" becomes "My_Swift_Class".Cardew
As for Xcode 6.3.1, it's not needed to make every class public. I managed to make it working as described above with "internal" classes.Introgression
@ArthurGevorkyan: Xcode 6.4, Swift 1.x here; trying to access AppDelegate from XCTestCase subclass (which imports MyAppTargetName ). Marking app delegate class and methods as public works OK; marking as internal gives "Use of undelcalred identifier AppDelegate" in XCTestCase subclass.Bernardabernardi
I tried: leaving AppDelegate and its properties/methods as internal, but modifying the target membership of AppDelegate.swift to include the test target too. This builds, but at runtime the test fails because let delegate = UIApplication.sharedApplication().delegate as? AppDelegate fails (either its nil, or an object of a different type - haven't checked ). If instead I make all public and remove target membership, it works.Bernardabernardi
[continued] it turns out it is of the wrong type, not nil! It is the cast that fails.Bernardabernardi
@NicolasMiari, does it mean that it's all right now with your setup? If yes, then congrats :)Introgression
@ErikP.Hansen you are a bawsVicious
B
73

UPDATE Swift 2.x, 3.x, 4.x and 5.x

Now you don't need to add the public to the methods to test then. On newer versions of Swift it's only necessary to add the @testable keyword.

PrimeNumberModelTests.swift

import XCTest
@testable import MyProject

class PrimeNumberModelTests: XCTestCase {
    let testObject = PrimeNumberModel()
}

And your internal methods can keep Internal

PrimeNumberModel.swift

import Foundation

class PrimeNumberModel {
   init() {
   }
}

Note that private (and fileprivate) symbols are not available even with using @testable.


Swift 1.x

There are two relevant concepts from Swift here (As Xcode 6 beta 6).

  1. You don't need to import Swift classes, but you need to import external modules (targets)
  2. The Default Access Control level in Swift is Internal access

Considering that tests are on another target on PrimeNumberModelTests.swift you need to import the target that contains the class that you want to test, if your target is called MyProject will need to add import MyProject to the PrimeNumberModelTests:

PrimeNumberModelTests.swift

import XCTest
import MyProject

class PrimeNumberModelTests: XCTestCase {
    let testObject = PrimeNumberModel()
}

But this is not enough to test your class PrimeNumberModel, since the default Access Control level is Internal Access, your class won't be visible to the test bundle, so you need to make it Public Access and all the methods that you want to test:

PrimeNumberModel.swift

import Foundation

public class PrimeNumberModel {
   public init() {
   }
}
Birck answered 25/8, 2014 at 1:26 Comment(7)
is there any way to change the default Access Control? I had a funny case that it used to run without public modifier just fine, then I moved the test cases around, it suddenlly stopped working anymore.Refinement
I think that's not possible, at least for the current version of swift.Birck
thanks. And I found out that my problem was that the swift file is not build to the test target with the test cases.Refinement
@Refinement you should't build the class on the test target, take a look on a post that I wrote about this in more detail: blog.diogot.com/blog/2014/08/23/…Birck
yes, that was exactly where I felt wrong -- I should not build the class swift file into the test case target. nice blog post! thanks.Refinement
If you have an project created with Xcode 5 or later and you are opting for the "public access, import target module" approach, double check the module name for your test target as it might be the same as the main target. If you are receiving a No such module <moduleName> compile error in your test case you may want to check the PRODUCT_MODULE_NAME for the test target. Great answer Diogo.Kelleykelli
Just in case someone else runs into this, you need to check where the settings are defined in the Project > Target hierarchy. Click the "Levels" option in the to left of the target settings editor (under the tab control) and make sure your application target has a PRODUCT_MODULE_NAME defined, but not the testing target. You might have accidentally defined the PRODUCT_MODULE_NAME at the project level instead of the target level, resulting in both your targets having the same module name.Nonreturnable
J
51

In the Documentation it says there are no import statements in Swift.

enter image description here

Simply use:

let primNumber = PrimeNumberModel()
Joellenjoelly answered 4/6, 2014 at 5:50 Comment(4)
This worked but I had to close and reopen Xcode 6.0 for the class to finally show up. Try cleaning and building the project as well.Elyseelysee
This seems to be new (or an issue) in the latest XCode 6.3 beta's, so the import statement is neccessary now. Also there is an import statement in Swift, to import modules.Oldwife
According the provided documentation, this table with "No import statement" is related to the "Import from the same target" case. In case of XCTests you use different (test) target, so observe the "Importing External Frameworks" section of the article, there is another table: Any language framework -> Import into Swift -> import "FrameworkName" is neededEngender
If you see an issue in the editor, it's often that there's a test target that doesn't have the class included. So you probably build your runnable fine, but if any one of the targets is having an issue, it still shows the error. If you had selected one of those other targets, it wouldn't let you compile.Morphophonemics
T
35

Check target-membership of PrimeNumberModel.swift in your testing target.

Thermit answered 9/6, 2014 at 5:58 Comment(7)
please add this as comment.Resist
This person can't, he needs 9 more rep to do so (at time of writing).Defenestration
What the heck is a target?Elastin
Thanks this worked for me. Click on the class file. Check the the first tab of inspector column on the right. The second section is "Target Membership". Make sure the target/product you're trying to include the class is is selected.Rotterdam
It's not strictly an answer to the question "how do I import...", but it did answer the question I had that brought me here (I forgot to add a new file to the target). So for me, and others that will arrive here by following the first google result for "swift unresolved identifier class", this is an answer (and not a comment).Lavish
This can't solve the question for many scenarios, what this does is make the test app create a separate instance of the class, so if you are for example testing services, or components that can only be connected to one instance at a time you'll end up creating conflicts or getting the wrong data.Sparkman
Including your app's code files in the test target can also cause all sorts of weird issues when your test target is compiling, such as this one. I did this at first then realized just importing the whole module is a better idea.Prather
P
19

In Objective-C, if you wanted to use a class in another file you had to import it:

#import "SomeClass.h"

However, in Swift, you don't have to import at all. Simply use it as if it was already imported.

Example

// This is a file named SomeClass.swift

class SomeClass : NSObject {

}

// This is a different file, named OtherClass.swift

class OtherClass : NSObject {
    let object = SomeClass()
}

As you can see, no import was needed. Hope this helps.

Pro answered 18/7, 2014 at 20:8 Comment(1)
Thanks! Still getting around all this new stuff from in Swift.Literary
T
6

According To Apple you don't need an import for swift files in the Same Target. I finally got it working by adding my swift file to both my regular target and test target. Then I used the bridging header for test to make sure my ObjC files that I referenced in my regular bridging header were available. Ran like a charm now.

import XCTest
//Optionally you can import the whole Objc Module by doing #import ModuleName

class HHASettings_Tests: XCTestCase {

override func setUp() {
    let x : SettingsTableViewController = SettingsTableViewController()

    super.setUp()
    // Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    super.tearDown()
}

func testExample() {
    // This is an example of a functional test case.
    XCTAssert(true, "Pass")
}

func testPerformanceExample() {
    // This is an example of a performance test case.
    self.measureBlock() {
        // Put the code you want to measure the time of here.
    }
}

}

SO make sure PrimeNumberModel has a target of your test Target. Or High6 solution of importing your whole module will work

Tatianna answered 4/6, 2014 at 22:39 Comment(1)
Does this work only for logic/model classes, or can I use it for the Application Delegate too?Bernardabernardi
C
6

I was able to solve this problem by cleaning my build.

Top menu -> Product -> Clean Or keyboard shortcut: Shift+Cmd+K

Choong answered 22/1, 2015 at 20:52 Comment(0)
P
5

As of Swift 2.0, best practice is:

Add the line @testable import MyApp to the top of your tests file, where "MyApp" is the Product Module Name of your app target (viewable in your app target's build settings). That's it.

(Note that the product module name will be the same as your app target's name unless your app target's name contains spaces, which will be replaced with underscores. For example, if my app target was called "Fun Game" I'd write @testable import Fun_Game at the top of my tests.)

Prather answered 5/1, 2016 at 5:51 Comment(0)
R
3

Check your PrimeNumberModelTests Target Settings.

If you can't see PrimeNumberModel.swift file in Build Phases/Compile Sources, add it.

Roaster answered 3/7, 2014 at 12:42 Comment(1)
This is not the right way to do it, check the High6 response.Birck
F
2

You need to add a routine for the compiler to reference as an entry point, so add a main.swift file, which in this case simply creates an instance of your test file:

main.swift

PrimeNumberModelTests()

Then compile on the command line (I am using El Capitan and Swift 2.2):

xcrun -sdk macosx swiftc -emit-executable -o PrimeNumberMain PrimeNumberModel.swift PrimeNumberModelTests.swift main.swift

In this case, you will get a warning: result of initializer is unused, but the program compiles and is executable:

./PrimeNumberMain

CAVEAT: I removed the import XCTest and XCTestCase type for simplicity.

Flabellate answered 27/6, 2016 at 3:48 Comment(0)
H
1

So, you need to

  1. Import external modules you want to use
  2. And make sure you have the right access modifiers on the class and methods you want to use.

In my case I had a swift file I wanted to unit test, and the unit test file was also a swift class. I made sure the access modifiers were correct, but the statement

import stMobile

(let's say that stMobile is our target name)

still did not work (I was still getting the 'No such module' error), I checked my target, and its name was indeed stMobile. So, I went to Build Settings, under packaging, and found the Product Module Name, and for some reason this was called St_Mobile, so I changed my import statement

import St_Mobile

(which is the Product Module Name), and everything worked.

So, to sum up:

  1. Check your Product Module Name and use the import statement below in you unit test class

    import myProductModuleName
    
  2. Make sure your access modifiers are correct (class level and your methods).

Highchair answered 4/10, 2014 at 11:37 Comment(0)
A
1

Instead of requiring explicit imports, the Swift compiler implicitly searches for .swiftmodule files of dependency Swift libraries.

Xcode can build swift modules for you, or refer to the railsware blog for command line instructions for swiftc.

Algebraist answered 2/12, 2014 at 15:37 Comment(0)
M
1

As @high6 and @erik-p-hansen pointed out in the answer given by @high6, this can be overcome by importing the target for the module where the PrimeNumberModel class is, which is probably the same name as your project in a simple project.

While looking at this, I came across the article Write your first Unit Test in Swift on swiftcast.tv by Clayton McIlrath. It discusses access modifiers, shows an example of the same problem you are having (but for a ViewController rather than a model file) and shows how to both import the target and solve the access modifier problem by including the destination file in the target, meaning you don't have to make the class you are trying to test public unless you actually want to do so.

Millais answered 4/1, 2015 at 20:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.