Best Practices to Use Swift For Backend

Best Practices to Use Swift For Backend

EngineeringSwift
Fix bugs faster! Log Collection Made Easy
START NOW!

In the last article, we gave a general introduction to using Swift for backend development. We discussed its advantages: its robustness, stability and, above all, its simplicity.

Now we’re going to drill a little deeper, and provide some best practices that will help you in your day-to-day work. Our work today will focus on Vapor, an open-source web framework that provides a robust foundation for websites, APIs and Cloud projects. If you have any questions, just reach to us and we’ll do our best to help.

First, let’s explore some best practices for testing backend services

There’s a lot of stuff we could discuss here, but here are four essential tips I personally find useful.

1) Automate tools for your testing process. Continuous Integration (CI) tools like GitHub Actions, GitLab CI and Jenkins can run on every commit or pull request, catching problems early and guaranteeing good code quality.

2) Aim for a good amount of tests, but understand that it’s not always feasible or necessary to reach 100% coverage. In fact, it’s best to focus only on the core critical paths and functionality that matters to your users. 3) Use performance testing to point out the bottlenecks or possible issues during scalability. I’d recommend using tools like Apache JMeter, which will simulate various loads on your application.

4) Look for possible vulnerabilities at all times. Static analysis or security-focused testing tools can fulfil this function in your CI/CD pipeline.

Ok, now let’s look at a unit testing example with XCTest

XCTest is the default unit testing framework for XCode, known for being both comprehensive and accessible. Here’s what a test with Vapor will look like.

// VATServices.swift

import Vapor

struct VATServices {
    func calculateVATTax(amountV: Double, rateV: Double) -> Double {
        return amountV * rateV / 100
    }
}

// VATServicesTest.swift

import XCTest
@testable import App

final class VATServicesTest: XCTestCase {
    func testCalculateVATTex() {
        let vatServices = VATServices()
        
        let result = vatServices.calculateVATTax(amountV: 200, rateV: 10)
        XCTAssertEqual(result, 20, "This VAT calculation should be good")
    }
}

Integration testing example with Vapor

We need to test the full process of receiving a request, processing it through our routes, and ensuring the response is as expected. This is where integration testing comes in.

Here’s an example of a test that checks a simple route and assures that it returns a 200 status code (meaning that the request was successful), with the message “Hello, world tested!”

The XCTVapor package provides the utilities to test requests and responses. Here’s an example.

// ApplicationTest.swift

import XCTVapor
@testable import App

final class ApplicationTest: XCTestCase {
    var app: Application!
    
    override func setUp() {
        super.setUp()
        app = Application(.testing)
        try! configure(app)
    }
    
    override func tearDown() {
        
        super.tearDown()
        app.shutdown()
    }
    
    func testTheHelloWorldRoute() throws {
        try app.test(.GET, "helloWorld", afterResponse: { res in
            XCTAssertEqual(res.status, .ok)
            XCTAssertEqual(res.body.string, "Hello, world Tested!")
        })
    }
}

Debugging Swift backend code

Debugging with LLDB

Ok, now let’s go a little further.

When we run a Swift backend app, we typically face issues with breakpoints and inspecting variables. Using a Low Level Debugger, we have the ability to pause the execution and test the state of the function when the program is running.

  1. Set a breakpoint in your code in the Xcode or by using breakpoint set command in LLDB.
  2. Run the application in debug mode. From the terminal, we can start LLDB with lldb .build/debug/ExecutableName.
  3. When execution pauses at the breakpoint, we should use LLDB commands to inspect variables or step through the code.

We can use**print variableName** to print the value of the variable, and**step** or next to move through the code line by line.

Logging for debugging

Logging into the application can help track down issues by providing runtime info. Here’s an example of a test.

import LoggingInfo
func route(_ app: Application) throws { app.get("helloRoute") { req -> String in
    req.logger.info("Hello this route was accessed")
    return "Hello, Bugfender world!"
}
}

Deploying & Scaling Swift Backend Services

Now, the fun part: we can implement continuous integration continuous deployment (CI/CD) pipelines, creating scalable architectural patterns in load balancing, microservices architecture, and serverless architecture. This is key to ensuring a strong, robust backend infrastructure.

Here are some popular deployment options:

Bare Metal

This gives you more control of the overall infrastructure, but it involves more effort in setup and installation. In fact, you must step up Swift and prepare your app to run on a dedicated Linux server.

The server configuration setup should be started on boot, and you should deploy reverse proxy settings with Nginx or Apache servers to handle HTTP requests. Docker

Containerisation with Docker will pack your app and its all dependencies into a container, assuring consistency from one environment to another. Here’s an example:


FROM swift:latest as builder
WORKDIR /app
COPY.
RUN swift build -c release
FROM swift:slim
WORKDIR /app
COPY --from=builder /app/.build/release /app
CMD ["./Bugfender"]

Cloud Services like AWS and Azure

Cloud technology provides the necessary managed services and infrastructure, allowing deployment and scaling of the application with less overhead.

Continuous Integration/Continuous Deployment (CI/CD) Pipelines for Swift

Now, let’s look at some CI/CD pipelines. Here’s a quick example:

GitHub Actions Sample code:

name: Swift CI/CD

on: 
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Set up Swift
      uses: fwal/setup-swift@v1

    - name: Build
      run: swift build

    - name: Testing
      run: swift test

    - name: Dockerize and Deploy
      env: 
        DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
      run: |
        docker build -t Bugfender .
        echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
        docker push Bugfender

Conclusion

Now you should have all the knowledge you need to start using Swift for backend projects. As well as a general overview of the tools at your disposal (and the strengths and weaknesses of each one), you’ve seen plenty of code that should help you get up and running. Hopefully these articles have been accessible, engaging, and fun.

But if you require any further guidance, or have any specific questions, don’t hesitate to get in touch. We love to talk about this kind of stuff (or anything dev-related really), so let’s chat!

Expect the Unexpected! Debug Faster with Bugfender
START FOR FREE

Trusted By

/assets/images/svg/customers/projects/taxify.svg/assets/images/svg/customers/projects/safedome.svg/assets/images/svg/customers/highprofile/kohler.svg/assets/images/svg/customers/cool/ubiquiti.svg/assets/images/svg/customers/highprofile/volkswagen.svg/assets/images/svg/customers/cool/levis.svg/assets/images/svg/customers/highprofile/intel.svg/assets/images/svg/customers/highprofile/dolby.svg

Already Trusted by Thousands

Bugfender is the best remote logger for mobile and web apps.

Get Started for Free, No Credit Card Required