Using functions defined in different files

Hi, I'm just starting out with swift and have structured my code into several files. I used swift package init --type executable command to lay out the directories. Inside Sources/myProj, I've put two different swift files with two different functions i.e. file1.swift and file2.swift. Inside these I've one function in each i.e. funct1 and func2.

Now I want to use these functions in my main.swift file but I can't figure out how to import them. Maybe someone can help me out here.

Thanks

In Swift, you don't import individual files. You can call functions anywhere in the same module.

Try the swift run command.

Thanks, it worked if all three swift files are in same directory. In my case I've them in separate folders inside Sources.

Here's how the peoject is structured:

-myProj
- Sources
    - myProj
            - main.swift
    - core
            - file1.swift
            - file2.swift
- Tests
- .gitignore
- Package.swift
- README.md

Each folder under Sources is a module, in your manifest file package.swift you should include core as a dependency of myProj target, then in main.swift you have to import the module import core.

I modified package.swift as follows

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "01_sum_two_digits",
    dependencies: [.package(path: "Sources/core")
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "01_sum_two_digits",
            dependencies: ["core"]),
        .testTarget(
            name: "01_sum_two_digitsTests",
            dependencies: ["01_sum_two_digits", "core"]),
    ]
)

and getting an error: 01_sum_two_digits/Sources/core has no Package.swift manifest.

That's because you are including Sources/core as a Package dependency, but Sources/core is not a Package itself. Core is only a target dependency, meaning you only have to add it as a dependency in your targets.

let package = Package(
    name: "01_sum_two_digits",
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "01_sum_two_digits",
            dependencies: ["core"]),
        .testTarget(
            name: "01_sum_two_digitsTests",
            dependencies: ["01_sum_two_digits", "core"]),
    ]
)

I did this first but got an error error: product 'core' not found. it is required by package '01_sum_two_digits' target '01_sum_two_digitsTests'.

Here's my code:

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "01_sum_two_digits",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "01_sum_two_digits",
            dependencies: ["core"]),
        .testTarget(
            name: "01_sum_two_digitsTests",
            dependencies: ["01_sum_two_digits", "core"]),
    ]
)

Oh sorry, you have to define core as a target there. Try something like:

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "01_sum_two_digits",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "01_sum_two_digits",
            dependencies: ["core"]),
        .target(name: "core"),
        .testTarget(
            name: "01_sum_two_digitsTests",
            dependencies: ["01_sum_two_digits", "core"]),
    ]
)
1 Like

I'm now getting error: cannot find 'addWithChecks' in scope. addWithChecks is a function inside file1.swift.

That's because declarations have access level internal by default, which means only available within the same module. You need to change your functions to public func for example.

Thanks, it works now. I still don't understand why I can't compile the main.swift* directly. It gives me error: no such module 'core'.

I've also put several stress tests in Tests folder, but I can't run them individually. It's so confusing.

I'm also confused, albeit probably for different reasons. I'm a casual, but I also don't know why you want to compile it manually like that.

But I did some digging and figured out that you can do this:

swiftc -emit-library -emit-module -module-name core Sources/core/**/*.swift

That will create the following files: core.swiftdoc, core.swiftmodule, core.swiftsourceinfo, libcore.dylib

swiftc -I . -L . -lcore Sources/myProj/main.swift

That will create the main executable.

So you could do it that way, but there's probably a bunch of reasons why swift build is preferred (I assume that it is), like incremental build optimizations and things like that (whatever the .build directory is for).

If Swift's import were file-based I guess it would simpler, but I don't know why it's designed the way it is, and there's probably good reasons for it (less boilerplate for example?).

Hope this helps.

That would make things much more complicated, because then you would have to change the import statement each time you move a function to a different file. And what about closed source binaries? You wouldn't have access to the source files, so there would be no way to know which file each symbol was declared in.

1 Like

I want to manually compile this because I've some custom stress tests written and stored in Tests/myProj folder.

- myProj
- Sources
    |- myProj
        |- main.swift
    |- core
        |- file1.swift
        |- file2.swift
- Tests
   |- myProjTests
        |- myProjTests.swift
        |- customTest1.swift
        |- customTest2.swift
        |- customTest3.swift
- .gitignore
- Package.swift
- README.md

Each of the customTest#.swift file checks individual functions written in file1.swift and file2.swift. I was hoping I could individually import functions (or use them individually) in these files one by one and test them separately. This is the reason I want to run these manually so that I can ensure that each function works well before integrating them into the main.swift file.

I was looking at swift test command but can't figure out how that works. Maybe there's a better way to do this instead of manually running everything. Please let me know if there's some substitute to this.

Hope this clears up what I want to do.

AFAICT this should work:

Put this in customTest1.swift:

import XCTest
@testable import core

final class customTest1: XCTestCase {
    func testFunc1() {
        let result = func1()
        XCTAssertEqual(result, "func1 says hello")
    }
}

Then you can run swift test or, to run only that specific test:

swift test --filter myProjTests.customTest1/testFunc1

You don't need to manually compile core into a module/lib for this, as long as "core" is in the testTarget dependencies in Package.swift.

Is that suitable for you?

Yes, it is!!! Thanks a lot!!!

I would like to know more about how swift packages does all this compiling and linking automatically (just curious), it would be great if you can share link to some docs.

Thanks a lot again.

1 Like

See

2 Likes

You really don't want to know this unless you're contributing the language itself. What you need to understand is the structure of a swift package and the commands you can run from the command line.

You can also use swift test --filter to run multiple specific tests using the following syntax:

swift test --filter 'customTest1/testFunc1|customTest1/testFunc2'

Separate each test using the | character.

1 Like