Due to the availability of the Swift Language Server Protocol (LSP) library, there are now more IDEs that you can use to develop server-side Swift applications, including VSCode on Linux. Undoubtedly though, most development is still done by using Xcode on macOS.
While this is great in terms of support for the Swift language — and with the support for Swift Package Manager added in Xcode 11, for package management — there is still the challenge of ensuring that the APIs, frameworks, and packages that you are using on macOS are supported and work in the same way on Linux.
The new support for Swift in the Appsody cloud-native development tools helps to solve that challenge, as you can continuously run and test your code in a Docker container as you develop in Xcode.
This means that, as you develop in Xcode, or any other IDE, your code is mirrored into a Docker container which rebuilds every time you save your code changes. You can see immediately whether your code changes fail or have different behavior on Linux.
The tutorial shows you how to run your project in the Appsody environment in parallel to working in Xcode.
To follow this tutorial, you need to install both the Appsody CLI and Docker. You also need a Swift application; the tutorial walks you through two ways of creating a Swift app if you don't have one already.
It is recommended that you install Docker by using Docker Desktop for Mac, which is available as a download from Docker Hub.
Install the Appsody CLI by following the Installing Appsody guide. On macOS, you can install using Homebrew:
brew install appsody/appsody/appsody
Next, you need a server-side Swift application. You can either use one you already have, or create a sample application by using one of the two following approaches:
You can create a sample application using the Kitura CLI by following the Create a server with the Kitura CLI guide, or you can use the Swift Package Manager by following the Create a server with Swift Package Manager guide. The following steps show how to use the CLI:
Install the Kitura CLI using Homebrew:
brew install ibm-swift/kitura/kitura
Create a directory for your Swift project:
mkdir ~/swiftapp cd ~/swiftapp
Create a Kitura application:
You can create a sample application using the Vapor CLI by following the Install on macOS guide to install the CLI, followed by the Hello, World guide to create your application. The following steps summarize those actions:
Install the Vapor CLI using Homebrew:
brew install vapor/tap/vapor
Make sure that you are in your home directory:
Create a Vapor application:
vapor new swiftapp cd swiftapp
Create an Xcode Project for your app (only required for Xcode 10 and before):
swift package generate-xcodeproj
New Appsody based applications are created by using
appsody init <stack> <template>, where the stack and template are both chosen by you from those that are listed when you run
appsody list. The
init command downloads the most recent copy of the Appsody Stack - essentially the development and build environment for that language - and populates the project directory with a template that provides a basic project structure.
To enable an existing application with a stack, the same steps can be followed, with one difference, you need to use a template name of
none with the
appsody init command.
Enable the Swift Application:
appsody init swift none
This will print output similar to the following to the console:
Running appsody init... Downloading swift template project from [https://github.com/appsody/stacks/releases/download/swift-v0.1.4/incubator.swift.templates.simple.tar.gz](https://github.com/appsody/stacks/releases/download/swift-v0.1.4/incubator.swift.templates.simple.tar.gz) Download complete. Extracting files from swift.tar.gz Setting up the development environment Running command: docker[pull appsody/swift:0.1] Running command: docker[run --rm --entrypoint /bin/bash appsody/swift:0.1 -c find /project -type f -name .appsody-init.sh] Successfully initialized Appsody project with the swift stack and no template.
init command downloads the latest version of the Appsody Stack (in this case version 0.1), and adds an .appsody-config.yaml file to the project, which defines the versions of the Stack that the project will use.
Your application is now enabled, which means that you can run, test and debug your application in the continuous containerized environment that is provided by the Appsody Stack. You can then build your app into an optimized container image, and deploy it to Kubernetes.
Now you can use the Appsody CLI's run, test, and debug commands to work with your application in the continuous containerized environment provided by the Appsody Stack.
appsody run command provides a continuous development environment, where changes that are saved to your project cause your application to be restarted.
Xcode 10 and earlier:
cd ~/swiftapp xed .
Xcode 11 and beyond:
cd ~/swiftapp open Package.swift
Next, start the continuous run environment:
appsody runcommand builds and runs your application in a Docker container. The first run might take a couple of minutes as it downloads and builds your application for the first time, but subsequent runs cache that data.
If you are running the Kitura sample app, the output is as follows:
However, if you are running Vapor, the web page fails to load and you see an error. Vapor apps accept requests from localhost only by default. The Docker container does not see your host machine (for example, your MacBook) as localhost, so you need to enable remote connections.
You can enable remote connections by adding the following code snippet into
Sources/App/app.swift in Xcode:
let serverConfig = NIOServerConfig.default(hostname: "0.0.0.0") services.register(serverConfig)
appsody run provides a continuous development environment, any code changes you make cause an automatic restart of the application. Go back to your browser and see if you can connect to your app now. The expected output is:
With Appsody, any code changes that you make are reflected immediately. If you’re using Vapor, you saw this feature when you enabled requests from non-localhost addresses to be handled.
For Kitura, a simple code change to demonstrate this is to remove the Health Route.
By default, Kitura provides a /health route that responds with the status of the Kitura server. It is provided because many clouds and cloud technologies, like Kubernetes, can use it to determine whether the application needs to be automatically restarted.
Sources/Application/Application.swift and remove the following line:
Save the file, and open http://localhost:8080/health to see that the route is no longer present!
Appsody’s test mode runs any tests that your application has inside the containerized environment.
Run the tests by using the Appsody test mode:
The command responds with the output from your tests:
[Container] Test Suite 'All tests' started at 2019-09-01 11:58:12.127 [Container] Test Suite 'debug.xctest' started at 2019-09-01 11:58:12.171 [Container] Test Suite 'RouteTests' started at 2019-09-01 11:58:12.172 [Container] Test Case 'RouteTests.testGetStatic' started at 2019-09-01 11:58:12.172 [Container] ------------------------------ [Container] ------------New Test---------- [Container] ------------------------------ ... [Container] Test Case 'RouteTests.testGetStatic' passed (0.21 seconds) [Container] Test Suite 'RouteTests' passed at 2019-09-01 11:58:12.382 [Container] Executed 1 test, with 0 failures (0 unexpected) in 0.21 (0.21) seconds [Container] Test Suite 'debug.xctest' passed at 2019-09-01 11:58:12.383 [Container] Executed 1 test, with 0 failures (0 unexpected) in 0.21 (0.21) seconds [Container] Test Suite 'All tests' passed at 2019-09-01 11:58:12.383 [Container] Executed 1 test, with 0 failures (0 unexpected) in 0.21 (0.21) seconds
Unlike run and debug, test executes a single run of the tests rather than a continuous container.
When you are ready to build a deployable container image for your application, you can do that using
appsody build. It creates a production-optimized image that is built by using the regular “swift” Official Docker image from the Swift community and is then rebased on the communities “slim” image. The “slim” image is both significantly smaller than the regular image, making it faster to deploy to a cloud, and more secure because it does not include the Swift compiler, it contains enough to run your application only.
The following steps show you how to build the container image for your application, and then run it locally using Docker:
Build the container image for your application:
It builds a container image for your application, using the name of your project as the tag (which is the name of the folder in which your app resides prepended with 'dev.local'). The final few lines of output include the name of the image:
Built docker image dev.local/swiftapp
To see the size of your built image, you can use
docker images. It shows that the built image is 315 MB. By comparison, when the image was built with the full Swift image it was 1.47 GB!
Run the container image using Docker:
docker run --rm -p 8080:8080 -i -t dev.local/swiftapp
The command runs your container image, using the
-p option to map port 8080 from the container to port 8080 on your machine, the
-i option to run interactively (so that you can use Ctrl-C to stop the container), the
--rm option to remove the container when it is stopped, and the
-t option to run the container image with the tag
Open your browser to see the application running: http://localhost:8080
Finally, stop the container by using Ctrl-C in the terminal window where
docker run is running.
You now have a cloud package available in a container image that is ready to be deployed to any cloud that supports container images.
If you have a Kubernetes cluster, you can easily deploy the built application using the
appsody deploy command.
You now know how to take your server-side Swift application and develop it with the benefit of a continuous Linux environment that is running in a Docker container locally on your MacBook.
Appsody provides similar experiences for many other languages and frameworks that include Java, Node.js and Python, as well as a mechanism to deploy and manage your application in Kubernetes.