Swift… Swift everywhere

darthpelo
7 min readMar 29, 2017

--

A simple web service, a Raspberry Pi, two LEDs and a native mobile application. Only one language: Swift.

Update 22/8/2018: I hope to have time to update the code base, btw for now remember that this project was wrote in 2017, using Vapor 2.0. Now with Vapor 3.0 the server side part of the project must be update.

Update 5/6/2017: I suggest to read the new post by uraimo about Swift 3.1.1 before my article, to be sure to set up correctly your Raspberry.

Update 9/4/2017: after the release of Swift 3.1 I encountered some issues with Vapor on the Raspberry Pi. I updated the RPiLedsControl README.md file with a solution.

Update 5/4/2017: I still have to thank Umberto Raimondi. He updated SwiftlyGPIO README.md with the solution for the sudo problem I encountered. I updated this post with the 4 steps to avoid this issue.

I am a software engineer and my first love was C and hardware. Last year when I discovered that someone was trying to build the Swift compiler for ARM architecture (that means compile Swift code on boards like Arduino or Raspberry), I was very excited about it. In my life as developer, for the first time, a compiled language, type safe, with very good performance, could be run everywhere. Without virtual machine or interpreters in the middle.

The idea

The main idea was to create a proof-of-concept to show how it’s possible to develop a full stack project (hardware controls, web services, mobile application) using only one language. To be clear, analyse all Swift server-side frameworks and find the best one it is out of scope, because of my lack of competence on the backend, I decided to choose the simplest solution, also because the idea is to run the backend… on a Raspberry Pi 2 :)

The idea becomes LedControl, a mono repo project with an iOS application and a web service that runs on a Raspberry Pi 2 and controls its GPIO. The project is work in progress, so could be that when you read this article, something is changed. Use the README.md files as main reference.

LedControl

LedControl is the union of three small sub components:

  • An iOS mobile application
  • A Vapor based service running on a Raspberry Pi 2
  • A logic to control Raspberry GPIO and switch on/off two 💡 based on my SwiftGPIOLibrary.

iOS Application

Begin a proof-of-concept, the scope of the application is only one:

turn on and off two led.

So its strucutre it’s minimal:

LedsControl mobile application structure

and also the UI 🙂

I used for the first time Moya as NetworkLayer, following the simple how-to I created MyService extension. It’s a very basic implementation with one goal: test the backend, so don’t blame me about it 😄

Raspberry Pi & Swift

Before showing you how to setup a server and how to control led, first we need a Raspberry ready for Swift.

It’s very simple:

👉 here there is the web page of the group that keep live Swift 3 for Raspberry 🤓 The istructions are clear and the process is very easy. Only one advice: you need to have Ubuntu 16.04 on your board.

Check if you have /dev/gpiomem If it is absent, following the instructions you can find here to use gpio without being root:

  1. As root create a new group called gpio (groupadd -f -r gpio)
  2. As root add the group to the linux user account, for example ubuntu or pi (usermod -G gpio ubuntu)
  3. As root create a udev rules file at /dev/gpiomem in /etc/udev/rules.d/99-gpio.rules
SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660"SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"

Before reboot the board to apply the changes, as root:

usermod -aG gpio ubuntu .

You can find more information inside the SwiftlyGPIO README.md file.

Vapor

After the Raspberry is Swift ready, we can choose which one of the open source Swift server-side frameworks use. When I started the project (October 2016), I had only experience with Perfect on Heroku, but at the time (July 2016) all the process of installing the framework and setup a server were for me, without any backend experience, was too complex. So I took the opportunity to try a new solution, Vapor.

Update: just to know, a collegue of mine told me that now Perfect is more easy to install and it’s more simple to create your server-side APIs.

Install Toolbox

Following these documentations I installed Vapor on a RPi 2 without any issues, literally with only one command:

curl -sL toolbox.vapor.sh | bash

Implement the endpoints

After that, I created a new project following the Hello Word! tutorial by Vapor. This tutorial is exhaustive and the best way to start to understand how Vapor handles HTTP requests and how to build&run a Vapor project.

To start I implemented two endpoints. One to turn on/off led and the other one to know the status of the led:


let drop = Droplet()
drop.get("cmd", ":id") { request in
guard let cmdId = request.parameters["id"]?.int else {
throw Abort.badRequest
}
// here the code to control the led ;) guard let json = returnJson(forCommand: cmdId) else {
throw Abort.badRequest
}
return json
}
drop.get("status") { request in
guard let json = returnJson(forCommand: nil) else {
throw Abort.badRequest
}
return json
}
drop.run()

For each valid GET cmd , the API returns a JSON with the :id and the status (on/off) of the two led connected to the Raspberry Pi. Same JSON result for the status request, but in this case no change to the led status.

An example:

how to switch on a led connected to BCM 20?
http://hostname:port/cmd/1
And this is the response
{“command”:”1",”green”:”1",”version”:”1.0.0",”yellow”:”1"}

Build and Run

To build your server run the following command in the project root folder:

vapor build serve

To run, again in root folder:

vapor run

IP and port

As default, Vapor runs the server locally (127.0.0.1) on the port 8080. For our purpose we have two option:

  • run the service at home, use the local IP of the Raspberry with the port 8080
  • expose the service outside your local network

Whatever is your solution you can start with this configuration json file in the Config folder of the Vapor project: server.json . If you want a better configuration take a look at the documentation 👉 https://vapor.github.io/documentation/guide/config.html

SwiftGPIOLibrary

After the mobile application written in Swift and the simple server-side service in Swift, it’s time to write (using Swift) the logic to interact with the real file 😉.

These ☝️ are the 💡 that we want to control:

  • yellow connected to the pin 20
  • green connected to the pin 26

The first step is to update Package.swift in the Vapor project with the SwiftGPIOLibrary package:

import PackageDescriptionlet package = Package(
name: "Hello",
dependencies: [
.Package(url: "https://github.com/darthpelo/SwiftGPIOLibrary.git", majorVersion: 0),
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 3)
],
exclude: [
"Config",
"Database",
"Localization",
"Public",
"Resources",
"Tests",
]
)

Then take a look at SwiftGPIOLibrary.swift to understand how the library works and which functions are implemented because it’s a WIP project, so could be that when you read this article I updated it 🙂.

Second step is to integrate the GPIO logic inside the server-side logic

Server-side project struct

Right now I adopted an easy solution with a singleton defined in GPIOService.swift file placed in the App folder.

Inside this class I defined which pins to use

private let list: [GPIOName] = [.P20, .P26]

and how to setup them as OUTPUT

func setup() {
self.ports = gpioLib.setupOUT(ports: list, for: .RaspberryPi2)
}

How to turn on/off a led?

fileprivate func switchYellow(_ cmd: Int) {
switch cmd {
case Command.One:
gpioLib.switchOn(ports: [.P20])
case Command.Two:
gpioLib.switchOff(ports: [.P20])
default:()
}
}

The cmd parameter is the command present in the query sent by the mobile application 🙂.

How to call switchYellow inside the server logic? Easy!

After you have created the Vapor droplet instance, create the GPIOService one and setup it

let drop = Droplet() let service = GPIOService.sharedInstance
service.setup()

And finally

drop.get(“cmd”, “:id”) { request in
guard let cmdId = request.parameters[“id”]?.int else {
throw Abort.badRequest
}
try service.execute(command: cmdId) guard let json = returnJson(forCommand: cmdId) else {
throw Abort.badRequest
}
return json
}

func execute(command: Int) throws is a simple switch that executes the specific function related to the command number.

Conclusion

The idea to create a proof-of-concept to show how it is possible to develop a full stack project (hardware controls, web services, mobile application) using only one language it’s not so crazy.

A lot of opportunity are open, for example right now I’m working to create a new step of interaction: an event on the mobile application, turns on a buzzer and the user must press a button, a real one 🔘, to switch off the buzzer. In the future after the user switches off the buzzer, the Raspberry will send a push notification to the application.

My proof-of-concept is very basic but I hope can give you the inspiration for something new ;)

You can find more information and the source code in the mono repo on github: https://github.com/darthpelo/LedControl.

Credits

Thanks to mogui Gianfranco Reppucci @Moxy85 and NatashaTheRobot for helping me with the review of this article 🙏.

--

--

darthpelo

Software Engineer & Senior iOS developer at @mobiquityinc NL. Craft Beer addict. Il caso non esiste. Il caos sì /The case does not exist. The chaos exists.