Organising your Swift source code using Swift Package Manager (SPM)

Being on a Mac my initial development was using Xcode. There is nothing wrong with that but occonially I would like to just use the command line. That is where SPM comes into play.
So suppose you want to make a new tool named smith you would take the following steps:


$ mkdir smith
$ cd smith
$ swift package init --type executable

After executing these commands the following structure will be created:


.
|-- Package.swift
|-- Sources
|   `-- main.swift
`-- Tests

Running this new structure is quite simple:


$ swift build
Compile Swift Module 'smith' (1 sources)
Linking ./.build/debug/smith
$ ./.build/debug/smith
Hello, world!

So I wanted to add a module that I would like to import into my source as a module.
So I created the following structure:


.
|-- Package.swift
|-- Sources
|   |-- MyModule
|   |   `-- mystruct.swift
|   `-- main.swift
`-- Tests

Where the file Sources/MyModule/mystruct.swift has the following content:


public struct MyStruct {
  public static let CONST = "MyConst"
}

And then Sources/swift.main uses the module like this:


import MyModule

print("Hello, world!")
print(MyStruct.CONST)

No rocket science, I just wanted to see how to compile this.


$ swift build
error: the package has an unsupported layout, unexpected source file(s) found: /Users/fred/tmp/smith/Sources/main.swift
fix: move the file(s) inside a module

So my next try was to move the main.swift into a subdirectory as well:


$ tree
.
|-- Package.swift
|-- Sources
|   |-- MyModule
|   |   `-- mystruct.swift
|   `-- smith
|       `-- main.swift
`-- Tests

And then:


$ swift build
Compile Swift Module 'smith' (1 sources)
Compile Swift Module 'MyModule' (1 sources)
/smith/Sources/smith/main.swift:1:8: error: no such module 'MyModule'
import MyModule
       ^
:0: error: build had 1 command failures
error: exit(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /smith/.build/debug.yaml

As it turns out you cane tell SPM that the directory MyModule is a dependency of smith in the Package.swift file.

Out of the box it looks like this:


// swift-tools-version:3.1

import PackageDescription

let package = Package(
    name: "smith"
)

So after changing this to:


// swift-tools-version:3.1

import PackageDescription

let package = Package(
    name: "smith",
    targets: [
        Target(
            name: "smith",
            dependencies: ["MyModule"]
        )
    ],
    dependencies: [
    ]
)

Everything now starts to work:


$ swift build
Compile Swift Module 'MyModule' (1 sources)
Compile Swift Module 'smith' (1 sources)
Linking ./.build/debug/smith
$ ./.build/debug/smith
Hello, world!
MyConst

Mission accomplished.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.