A Swift library for hardware projects on Linux/ARM boards with support for GPIOs/SPI/I2C/PWM/UART/1Wire.

Overview

SwiftyGPIO

A Swift library for hardware projects on Linux/ARM boards with support for GPIOs/SPI/I2C/PWM/UART/1Wire.

Summary

This library provides an easy way to interact with external sensors and devices using the digital GPIOs, SPI/I2C interfaces, 1-Wire buses, PWM signals and serial ports that boards like the Raspberry Pi provide, on Linux using Swift.

Like Android Things or similar libraries in Python, SwiftyGPIO provides the basic functionalities you'll need to control different devices: sensors, displays, input devices like joypads, RGB led strips and matrices.

You'll be able to configure port attributes and read/write the current GPIOs value, use the SPI interfaces (via hardware if your board provides them or using software big-banging SPI), comunicate over a bus with I2C, generate a PWM to drive external displays, servos, leds and more complex sensors, interact with devices that expose UART serial connections using AT commands or custom protocols, and finally connect to 1-Wire devices.

While you'll still be able to develop your project with Xcode or another IDE, the library is built to run exclusively on Linux ARM Boards (RaspberryPis, BeagleBones, ODROIDs, OrangePis, etc...).

Examples of device libraries and complete projects built using SwiftyGPIO that you can use as inspiration for your own DIY hardware projects are listed below, have fun!

Content:

Supported Boards

The following boards are supported and have been tested with recent releases of Swift:

  • Raspberry Pi 4, 4B
  • Raspberry Pi 3, 3A+, 3B+
  • Raspberry Pi 2 (Thanks to @iachievedit)
  • Raspberry Pi Zero 2 W
  • Raspberry Pi Zero W
  • Raspberry Pi Zero (Thanks to @MacmeDan)
  • Raspberry Pi Classic A,B,A+,B+ Rev1/Rev2
  • BeagleBones (Thanks to @hpux735)
  • OrangePi (Thanks to @colemancda)
  • OrangePi Zero (Thanks to @eugeniobaglieri)
  • Asus Tinkerboard (Thanks to Ernesto Lo Valvo)
  • C.H.I.P.

But basically everything that has an ARMv7/8+Ubuntu/Debian/Raspbian or an ARMv6+Raspbian/Debian should work if you can run Swift on it.

Please keep in mind that Swift on ARM is a completely community-driven effort, and that there are a multitude of possible board+OS configurations, don't expect that everything will work right away on every configuration even if most of the times it does, especially if you are the first to try a new configuration or board.

Installation

To use this library, you'll need a Linux ARM(ARMv7/8 or ARMv6) board with Swift 3.x/4.x/5.x.

If you have a RaspberryPi (A,B,A+,B+,Zero,ZeroW,2,3,4) with Ubuntu or Raspbian, get Swift 5.x from here or follow the instruction from buildSwiftOnARM to build it yourself in a few hours.

I always recommend to try one of the latest binaries available (either Ubuntu or Raspbian) before putting in the time to compile it yourself, those binaries could(and do most of the times) also work on other Debian-bases distibutions and on different boards.

An alternative way to get these Swift binaries on your Raspberry Pi is through the Swift on Balena project that provides well organized IoT focused Docker images.

You can also setup a cross-compiling toolchain and build ARM binaries (Ubuntu/Raspbian) from a Mac, thanks again to the work of Helge Heß (and Johannes Weiß for implementing it in SPM), read more about that here.

To start your project add SwiftyGPIO as a dependency in your Package.swift:

// swift-tools-version:4.0
import PackageDescription

let package = Package(
    name: "light",
    dependencies: [
         .package(url: "https://github.com/uraimo/SwiftyGPIO.git", from: "1.0.0")
    ]
)

And then build with swift build.

The compiler will create an executable under .build/debug/MyProject.

IMPORTANT: Like every library using GPIOs/SPI/I2C/etc..., if your OS does not come with a predefined user group to access these functionalities, you'll need to run your application with root privileges using sudo. If you are using a RaspberryPi with a Raspbian or a recent Ubuntu (from 16.04 Xenial onward) implementing /dev/gpiomem, sudo will be not required to use basic GPIOs, just launch your application calling the executable built by the compiler.

On misconfigured systems, features like the listeners may require root privileges too and advanced features like PWM sadly always require root privileges.

Alternatively, a specific user group for gpio access can be configured manually as shown here or in this answer on stackoverflow. After following those instruction, remember to add your user (e.g. pi) to the gpio group with sudo usermod -aG gpio pi and to reboot so that the changes you made are applied.

Your First Project: Blinking leds and sensors

If you prefer starting with a real project instead of just reading documentation, you'll find some ready to run examples under Examples/ and more than a few tutorials, full projects and videos available online:

Usage

Currently, SwiftyGPIO expose GPIOs, SPIs(if not available a bit-banging VirtualSPI can be created), I2Cs, PWMs, 1-Wire and UART ports, let's see how to use them.

GPIO

Let's suppose we are using a Raspberry 3 board and have a led connected between the GPIO pin P2 (possibly with a resistance of 1K Ohm or so in between) and GND and we want to turn it on.

Note that SwiftyGPIO uses the raw Broadcom numbering scheme (described here) to assign a number to each pin.

First, we need to retrieve the list of GPIOs available on the board and get a reference to the one we want to modify:

import SwiftyGPIO

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi3)
var gp = gpios[.P2]!

The following are the possible values for the predefined boards:

  • .RaspberryPiRev1 (Pi A,B Revision 1, pre-2012, 26 pin header)
  • .RaspberryPiRev2 (Pi A,B Revision 2, post-2012, 26 pin header)
  • .RaspberryPiPlusZero (Raspberry Pi A+ and B+, Raspberry Zero/W, all with a 40 pin header)
  • .RaspberryPi2 (Raspberry Pi 2 with a 40 pin header)
  • .RaspberryPi3 (Raspberry Pi 3 with a 40 pin header)
  • .RaspberryPi4 (Raspberry Pi 4 with a 40 pin header)
  • .BeagleBoneBlack (BeagleBone Black)
  • .CHIP (the $9 C.H.I.P. computer).
  • .OrangePi
  • .OrangePiZero

The map returned by GPIOs(for:) contains all the GPIOs of a specific board as described by these diagrams.

Alternatively, if our board is not supported, each single GPIO object can be instantiated manually, using its SysFS GPIO Id:

var gp = GPIO(name: "P2",id: 2)  // User defined name and GPIO Id

The next step is configuring the port direction, that can be either GPIODirection.IN or GPIODirection.OUT, in this case we'll choose .OUT:

gp.direction = .OUT

Then we'll change the pin value to the HIGH value "1":

gp.value = 1

That's it, the led will turn on.

Now, suppose we have a switch or a button connected to P2 instead, to read the value coming in the P2 port, the direction must be configured as .IN and the value can be read from the value property:

gp.direction = .IN
let current = gp.value

Some boards like the RaspberryPi allow to enable a pull up/down resistance on some of the GPIO pins to connect a pin to 3.3V (.up), 0V (.down) or leave it floating (.neither) by default when external devices are disconnected, to enable it just set the pull property:

gp.direction = .IN
gp.pull = .up

The pull state can only be set and not read back.

The other properties available on the GPIO object (edge,active low) refer to the additional attributes of the GPIO that can be configured but you will not need them most of the times. For a detailed description refer to the kernel sysfs documentation.

The GPIO object also supports the execution of closures when the value of the pin changes. Closures can be added with the methods onRaising (the pin value changed from 0 to 1), onFalling (the value changed from 1 to 0) and onChange (the value simply changed from the previous one):

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi3)
var gp = gpios[.P2]!


gp.onRaising{
    gpio in
    print("Transition to 1, current value:" + String(gpio.value))
}
gp.onFalling{
    gpio in
    print("Transition to 0, current value:" + String(gpio.value))
}
gp.onChange{
    gpio in
    gpio.clearListeners()
    print("The value changed, current value:" + String(gpio.value))
}  

The closure receives as its only parameter a reference to the GPIO object that has been updated so that you don't need to use the external variable. Calling clearListeners() removes all the closures listening for changes and disables the changes handler. While GPIOs are checked for updates, the direction of the pin cannot be changed (and configured as .IN), but once the listeners have been cleared, either inside the closure or somewhere else, you are free to modify it.

Setting the bounceTime property will enable software debounce, that will limit the number of transitions notified to the closure allowing only one event in the specified time interval in seconds.

The following example allows only one transition every 500ms:

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi3)
var gp = gpios[.P2]!

gp.bounceTime = 0.5
gp.onRaising{
    gpio in
    print("Transition to 1, current value:" + String(gpio.value))
} 

This functionality is extremely useful when using switches, that tend to generate multiple value spikes when the switch is pressed due to the mechanical characteristics of the compoment.

SPI

If your board has a SPI connection and SwiftyGPIO has it among its presets, a list of the available SPI channels can be obtained by calling hardwareSPIs(for:) with one of the predefined boards.

On RaspberryPi and other boards the hardware SPI SysFS interface is not enabled by default, check out the setup guide on wiki to enable it if needed using raspi-config.

Let's see some examples using a RaspberryPi 3 that has two bidirectional SPIs, managed by SwiftyGPIO as two SPIObjects:

let spis = SwiftyGPIO.hardwareSPIs(for:.RaspberryPi3)!
var spi = spis[0]

The interface is composed by 3 wire: a clock line (SCLK), an input line (MISO) and an output line (MOSI). One or more CS pins (with inverse logic) are available to enable or disable slave devices.

Alternatively, we can create a software SPI using four GPIOs, one that will serve as clock pin (SCLK), one as chip-select (CS or CE) and the other two will be used to send and receive the actual data (MOSI and MISO). This kind of bit-banging SPI is slower than the hardware one, so, the recommended approach is to use hardware SPIs when available.

To create a software SPI, just retrieve two pins and create a VirtualSPI object:

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi3)
var cs = gpios[.P27]!
var mosi = gpios[.P22]!
var miso = gpios[.P4]!
var clk = gpios[.P17]!

var spi = VirtualSPI(mosiGPIO: mosi, misoGPIO: miso, clockGPIO: clk, csGPIO: cs)

Both objects implement the same SPIObject protocol and so provide the same methods. To distinguish between hardware and software SPIObjects, use the isHardware property.

To send one or more byte over a SPI, use the sendData method. In its simplest form it just needs an array of UInt8 as parameter:

spi?.sendData([UInt(42)], frequencyHz: 500_000)

The frequency at which the data will be sent can be specified if needed (alternatively the default will be used, that is 500khz for hardware SPIs and the best available speed for virtual SPIs).

Since the interface performs only full duplex transmissions, to read some data from the SPI you'll need to write the same amount of bits. For most devices you'll use this means that you'll need to send some dummy data depending on the protocol used by your device. Check the device reference for more information.

Let's see a simple example, that reads 32 bytes from a device sending just 32 empty bytes:

let data = [ UInt8 ](repeating: 0, count: 32)
let res  = spi?.sendDataAndRead(data)

The res array will contain the raw data received from the device. Again, what to send and how the received data should be interpreted depends from the device or IC you are using, always read the reference manual.

I2C

The I2C interface can be used to communicate using the SMBus protocol on a I2C bus, reading or writing registers on devices identified by a numerical address. This interface needs just two wires (clock and data) and unlike SPI, it does not need a dedicated chip select/enable wire to select which device will receive the signal being sent, since the address of the destination of the protocol's messages is contained in the message itself, quite an improvement.

To obtain a reference to the I2CInterface object, call the hardwareI2Cs(for:) utility method of the SwiftyGPIO class:

let i2cs = SwiftyGPIO.hardwareI2Cs(for:.RaspberryPi3)!
let i2c = i2cs[1]

On Raspberry Pi and other boards this interface could not enabled by default, always verify its state checking the setup guide on the wiki to enable it if needed using raspi-config.

This object provide methods to read and write registers of different sizes and to verify that a device at a certain address is reachable or to enable a CRC on the protocol's messages:

func isReachable(_ address: Int) -> Bool
func setPEC(_ address: Int, enabled: Bool)

You should choose the read method to use depending on whatever of not your device supports multiple registers (command in SMBus parlance) and depending of the size of the register you are going to read from:

func readByte(_ address: Int) -> UInt8
func readByte(_ address: Int, command: UInt8) -> UInt8
func readWord(_ address: Int, command: UInt8) -> UInt16
func readData(_ address: Int, command: UInt8) -> [UInt8]
func readI2CData(_ address: Int, command: UInt8) -> [UInt8]

Reading and writing data blocks supports two modes, a standard SMBus mode (readData and writeData) that prepends the length of the block before the actual data, and an old style I2C mode (readI2CData and writeI2CData) that just send the data without additional metadata. Depending on the device, only one of the two modes will be supported.

Let's suppose that we want to read the seconds register (id 0) from a DS1307 RTC clock, that has an I2C address of 0x68:

print(i2c.readByte(0x68, command: 0)) //Prints the value of the 8bit register

You should choose the same way one of the write functions available, just note that writeQuick is used to perform quick commands and does not perform a normal write. SMBus's quick commands are usually used to turn on/off devices or perform similar tasks that don't require additional parameters.

func writeQuick(_ address: Int)

func writeByte(_ address: Int, value: UInt8)
func writeByte(_ address: Int, command: UInt8, value: UInt8)
func writeWord(_ address: Int, command: UInt8, value: UInt16)
func writeData(_ address: Int, command: UInt8, values: [UInt8])
func writeI2CData(_ address: Int, command: UInt8, values: [UInt8])

While using the I2C functionality doesn't require additional software to function, the tools contained in i2c-tools are useful to perform I2C transactions manually to verify that everything is working correctly.

For example, I recommend to always check if your device has been connected correctly running i2cdetect -y 1. More information on I2C, and configuration instruction for the Raspberry Pi, are available on Sparkfun.

The Example/ directory contains a Swift implementation of i2cdetect and could be a good place to start experimenting.

The docs/ directory contains instead a simple guide to debug communication issues with I2C devices.

PWM

PWM output signals can be used to drive servo motors, RGB leds and other devices, or more in general, to approximate analog output values (e.g. generate values as if they where between 0V and 3.3V) when you only have digital GPIO ports.

If your board has PWM ports and is supported (at the moment only RaspberryPi boards), retrieve the available PWMOutput objects with the hardwarePWMs factory method:

let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi3)!
let pwm = (pwms[0]?[.P18])!

This method returns all the ports that support the PWM function, grouped by the PWM channel that controls them.

You'll be able to use only one port per channel and considering that the Raspberries have two channels, you'll be able to use two PWM outputs at the same time, for example GPIO12 and GPIO13 or GPIO18 and GPIO19.

Once you've retrieved the PWMOutput for the port you plan to use you need to initialize it to select the PWM function. On this kind of boards, each port can have more than one function (simple GPIO, SPI, PWM, etc...) and you can choose the function you want configuring dedicated registers.

pwm.initPWM()

To start the PWM signal call startPWM providing the period in nanoseconds (if you have the frequency convert it with 1/frequency) and the duty cycle as a percentage:

print("PWM from GPIO18 with 500ns period and 50% duty cycle")
pwm.startPWM(period: 500, duty: 50)

Once you call this method, the PWM subsystem of the ARM SoC will start generating the signal, you don't need to do anything else and your program will continue to execute, you could insert a sleep(seconds) here if you just want to wait.

And when you want to stop the PWM signal call the stopPWM() method:

pwm.stopPWM()

If you want to change the signal being generated, you don't need to stop the previous one, just call startPWM with different parameters.

This feature uses the M/S algorithm and has been tested with signals with a period in a range from 300ns to 200ms, generating a signal outside of this range could lead to excessive jitter that could not be acceptable for some applications. If you need to generate a signal near to the extremes of that range and have an oscilloscope at hand, always verify if the resulting signal is good enough for what you need.

Pattern-based signal generator via PWM

This functionality leverages the PWM to generate digital signals based on two patterns representing a 0 or a 1 value through a variation of the duty cycle. Let's look at a practical example to better understand the use case and how to use this signal generator:

Let's consider for example the WS2812/NeoPixel (see the dedicated library), a led with integrated driver used in many led strips.

This led is activated with a signal between 400Khz and 800Khz containing a series of encoded 3 byte values representing respectively the Green,Blue and Red color components, one for each led. Each bit of the color component byte will have to be encoded this way:

  • Bit value 0: A 1250ns signal that stays, at least, high for 350ns(T0H) and then low for 900ns(T0L), with a tollerance of 150ns.
  • Bit value 1: A 1250ns signal that stays,at least, high for 650ns(T1H) and then low for 600ns(T0L), with a tollerance of 150ns.

And once the whole sequence of colors for your strip of leds has been sent, you'll need to keep the voltage at 0 for 50us, before you'll be able to transmit a new sequence. The bytes sent will configure the leds of the strip starting from the last one, going backwards to the first one.

This diagram from the official documentation gives you a better idea of what those signals look like, based on the T0H,T0L,T1H,T1L defined earlier:

ws2812 timings

You could think to just send this signal based on those 0 and 1 pattern changing the values of a GPIO, but it's actually impossible for an ARM board to keep up with the rate required by devices like the WS2812 leds and trying to generate these signals in software introduces significant jitter too.

Once the period of the pattern is lower than 100us or so you need another way to send these signals.

And this is the problem that the pattern-based signal generator solves, leveraging PWM-capable output pins.

You'll find a complete example under Examples/PWMPattern, but let's describe each one of the steps needed to use this feature.

In this brief guide I'm using an 8x8 led matrix with 64 WS2812 leds (these matrices are usually marketed as NeoPixel matrix, Nulsom Rainbow matrix, etc... and you can find one of these in some Pimoroni products like the UnicornHat).

First of all let's retrieve a PWMOutput object and then initialize it:

let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi3)!
let pwm = (pwms[0]?[.P18])!

// Initialize PWM
pwm.initPWM()

We'll then configure the signal generator specifying the frequency we need (800KHz for a 1250ns pattern period), the number of leds in the sequence (I'm using and 8x8 led matrix here), and the duration of the reset time (55us). We'll call the initPWMPattern to configure these parameters. We specify the duty cycle (percentage of the period at which the pattern should have a high value) for the 0 and 1 values.

let NUM_ELEMENTS = 64
let WS2812_FREQ = 800000 // 800Khz
let WS2812_RESETDELAY = 55  // 55us reset

pwm.initPWMPattern(bytes: NUM_ELEMENTS*3, 
                   at: WS2812_FREQ, 
                   with: WS2812_RESETDELAY, 
                   dutyzero: 33, dutyone: 66) 

Once this is done, we can start sending data, this time we are using a function that sets the colors and another function that turn them in a series of UInt8 in the GBR format:

func toByteStream(_ values: [UInt32]) -> [UInt8]{
    var byteStream = [UInt8]()
    for led in values {
        // Add as GRB, converted from RGB+0x00
        byteStream.append(UInt8((led >> UInt32(16))  & 0xff))
        byteStream.append(UInt8((led >> UInt32(24)) & 0xff))
        byteStream.append(UInt8((led >> UInt32(8))  & 0xff))
    }
    return byteStream
}

var initial = [UInt32](repeating:0x50000000, count:NUM_ELEMENTS)
var byteStream: [UInt8] = toByteStream(initial)

pwm.sendDataWithPattern(values: byteStream)

The method sendDataWithPatter will use the sequence of UInt8 to produce a signal composed by the patterns described above.

We can then wait until the signal is completely sent and then perform the necessary final cleanup:

// Wait for the transmission to end
pwm.waitOnSendData()

// Clean up once you are done with the generator
pwm.cleanupPattern()

At this point you could configure a different signal calling again initPWMPattern if you want to.

UART

If your board support the UART serial ports feature (disable the login on serial with raspi-config for RaspberryPi boards), you can retrieve the list of available UARTInterface with SwiftyGPIO.UARTs(for:):

let uarts = SwiftyGPIO.UARTs(for:.RaspberryPi3)!
var uart = uarts[0]

On Raspberry Pi and other boards this interface could not enabled by default, always verify its state checking the setup guide on the wiki to enable it if needed using raspi-config.

Before we can start trasmitting data, you need to configure the serial port, specifying: the speed (from 9600bps to 115200bps), the character size (6,7 or 8 bits per character), the number of stop bits (1 or 2) and the parity of your signal (no parity, odd or even). Software and hardware flow control are both disabled when using this library.

uart.configureInterface(speed: .S9600, bitsPerChar: .Eight, stopBits: .One, parity: .None)

Once the port is configured you can start reading or writing strings of sequence of UInt8 with one of the specific methods of UARTInterface:

func readString() -> String
func readData() -> [CChar]
func writeString(_ value: String)
func writeData(_ values: [CChar])

func hasAvailableData() throws -> Bool
func readLine() -> String

A method to know if there is available data on the UART serial port and a specific method that reads lines of text (\n is used as line terminator, the serial read is still non-canonical) are also provided.

1-Wire

If your board provides a 1-Wire port (right now only RaspberryPi boards), you can retrieve the list of available OneWireInterface with SwiftyGPIO.hardware1Wires(for:):

let onewires = SwiftyGPIO.hardware1Wires(for:.RaspberryPi3)!
var onewire = onewires[0]

To retrieve the string identifiers associated to the devices connected to the 1-Wire bus, call getSlaves().

The data provided by these devices can then be retrieved via readData(slaveId:) using one of the identifiers obtained at the previous step.

The data coming from the device is returned by the Linux driver as a series of lines, most of which will be just protocol data you should just ignore. Check out the reference of your sensor to know how to interpret the formatted information.

Examples

Examples for different boards and functionalities are available in the Examples directory, you can just start from there modifying one of those.

The following example, built to run on the C.H.I.P. board, shows the current value of all the attributes of a single GPIO port, changes direction and value and then shows again a recap of the attributes:

let gpios = SwiftyGPIO.GPIOs(for:.CHIP)
var gp0 = gpios[.P0]!
print("Current Status")
print("Direction: "+gp0.direction.rawValue)
print("Edge: "+gp0.edge.rawValue)
print("Active Low: "+String(gp0.activeLow))
print("Value: "+String(gp0.value))

gp0.direction = .OUT
gp0.value = 1

print("New Status")
print("Direction: "+gp0.direction.rawValue)
print("Edge: "+gp0.edge.rawValue)
print("Active Low: "+String(gp0.activeLow))
print("Value: "+String(gp0.value))

This second example makes a led blink with a frequency of 150ms:

import Glibc

let gpios = SwiftyGPIO.GPIOs(for:.CHIP)
var gp0 = gpios[.P0]!
gp0.direction = .OUT

repeat{
	gp0.value = (gp0.value == 0) ? 1 : 0
	usleep(150*1000)
}while(true) 

We can't test the hardware SPI with the CHIP but SwiftyGPIO also provide a bit banging software implementation of a SPI interface, you just need two GPIOs to initialize it:

let gpios = SwiftyGPIO.GPIOs(for:.CHIP)
var sclk = gpios[.P0]!
var dnmosi = gpios[.P1]!

var spi = VirtualSPI(dataGPIO:dnmosi,clockGPIO:sclk) 

pi.sendData([UInt8(truncatingBitPattern:0x9F)]) 

Notice that we are converting the 0x9F Int using the constructor UInt8(truncatingBitPattern:), that in this case it's not actually needed, but it's recommended for every user-provided or calculated integer because Swift does not support implicit truncation for conversion to smaller integer types, it will just crash if the Int you are trying to convert does not fit in a UInt8.

Built with SwiftyGPIO

A few projects and libraries built using SwiftyGPIO. Have you built something that you want to share? Let me know!

Libraries

Libraries for specific devices.

  • SwiftFlowMeter - A Swift library for using Hall effect based water flow sensors.
  • SwiftyXBee - Library for the XBee module to communicate with Zigbee devices in API mode.
  • SwiftyOLED - Library for OLED displays based on SSD1306 and SSD1305.
  • SHT20 - Library for the I2C SHT20 Humidity and Temperature Sensor.
  • LSM303 - Triple-axis Accelerometer+Magnetometer (Compass) I2C board library.
  • PCA9685 - 16-Channel 12-bit PWM/Servo Driver PCA9685 I2C board library.
  • TM1637 - Library for the TM1637 7-segment driver chip.
  • HC-SR04 Ultrasonic sensors - Library for the HC-SR04 ultrasonic ranging sensor.
  • HT16K33 Leds - Project that uses the HT16K33 to drive led matrices and segment displays via I2C.
  • WS281x Leds - Library for WS2812x (WS2811,WS2812,WS2812B) RGB led strips, rings, sticks, matrices, etc...
  • Nokia5110(PCD8544) 128x64 LCD - Show text and graphics on a Nokia 3110/5110 LCD display.
  • HD44780U Character LCD - Show text on character LCDs controlled by the HD44780 or one of its clones.
  • DHTxx Temperature Sensor #1, #2 - Read temperature and humidity values from sensors of the DHT family (DHT11, DHT22, AM2303).
  • SG90 Servo Motor - Drives a SG90 servo motor via PWM but can be easily modified to use other kind of servos.
  • MCP3008 10 bits ADC - Convert analog values to integers with this SPI-driven ADC.
  • u-Blox GPS Receivers - Get location data from boards with the u-Blox 6/7/8 family of A-GPS receivers with an UART serial connection (e.g. NEO6M).
  • MPU-6050 Accelerometer/Gyro - Library for the MPU-6050 (and MPU-6000 family) Accelerometer and Gyroscope.
  • DS1307 RTC - Library for the DS1307 (DS1302, DS3231) I2C Real-Time Clock.
  • Wii Nunchuck - Library for the Wii Nunchuck controller.
  • RCWL-0516 - Library for the RCWL-0516 Microwave Radar.
  • DS18B20 - Library for the DS18B20 temperature sensor.
  • AMG88xx - Library for AMG88 thermopile sensors.

Awesome Projects

Complete IoT projects.

Support libraries

Additional libraries that could be useful for your IoT projects.

  • SwiftyGFX - A library with generic graphical functions useful when working with dot matrix displays.
  • PureSwift's Bluetooth - A suite of projects to add Bluetooth functionality to your Linux projects.

Additional documentation

Additional documentation, mostly implementation details, can be found in the docs directory.

Comments
  • UART runtime error

    UART runtime error

    Board Type: Raspberry Pi 4B

    Operating System: raspios_lite_arm64

    Swift Version: 5.5

    Description

    Just a new Package with Hello world!. import Foundation // NB import Glibc doesn't make any difference on this project or others I've built. import SwiftyGPIO print("Hello, world!")

    RUN TIME ERRORS:

    /home/pi/IOTests/.build/checkouts/SwiftyGPIO/Sources/UART.swift:44:31: error: consecutive statements on a line must be separated by ';' .RaspberryPiZero2, ^ ; /home/pi/IOTests/.build/checkouts/SwiftyGPIO/Sources/UART.swift:44:31: error: expected expression .RaspberryPiZero2, ^ /home/pi/IOTests/.build/checkouts/SwiftyGPIO/Sources/UART.swift:44:15: error: reference to member 'RaspberryPiZero2' cannot be resolved without a contextual type .RaspberryPiZero2, ~^~~~~~~~~~~~~~~~ [10/13] Compiling SwiftyGPIO Presets.swift error: fatalError

    opened by ea7kir 19
  • Failing to recognize UARTs on Raspberry Pi 3

    Failing to recognize UARTs on Raspberry Pi 3

    Board Type

    Raspberry Pi 3, planning to test on Raspberry Pi Zero W.

    Operating System

    Raspian

    Swift Version

    Swift 5.1

    Description

    Raspberry Pi 3 with 5in XPT2046 Touch Controller and a usb cable plugged in between the Pi and an Arduino Nano. The Arduino Nano is running pretested code reading values from a circuit and sending those values over its Serial connection to the Pi.

    I received the following error:

    Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /home/pi/Swish/pi.reidchatham.com/.build/checkouts/SwiftyGPIO/Sources/UART.swift, line 46
    

    Code for the Pi is here: https://github.com/rchatham/pi.reidchatham.com/blob/master/Sources/App/Models/Keypad.swift

    opened by rchatham 13
  • Adding support for hardware PWM

    Adding support for hardware PWM

    PWM outputs have a few uses (for hobby projects mainly driving motors and playing sounds) and a convenient sysfs interface. Implementation should be really straightforward.

    enhancement 
    opened by uraimo 13
  • I2C Device Not Detected (SHT20)

    I2C Device Not Detected (SHT20)

    Board Type

    Raspberry Pi 3B+

    Operating System

    Raspbian

    Swift Version

    Swift 4.2.3, prebuilt binary from https://github.com/uraimo/buildSwiftOnARM/releases/tag/4.2.3

    Description

    I have a SHT20 I2C sensor that I am trying to connect and use with the help of this library. According to the sensors data sheet the default address is 0x40 which es exactly what I get when I run:

    sudo i2cdetect -y 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --
    

    With this, at least I know that the device is connected correctly and not a connection problem.

    But now, when running the following code (extracted from the example in the library), I don't get anything back, and even checking directly for i2c.isReachable(0x40) returns false, so I can't get it to work 😢

    import SwiftyGPIO
    
    let i2cs = SwiftyGPIO.hardwareI2Cs(for:.RaspberryPi3)!
    let i2c = i2cs[1]
    
    print("Detecting devices on the I2C bus:\n")
    outer: for i in 0x0...0x7 {
        if i == 0 {
            print("    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f")
        }
        for j in 0x0...0xf {
            if j == 0 {
                print(String(format:"%x0",i), terminator: "")
            }
            // Test within allowed range 0x3...0x77
            if (i==0) && (j<3) {print("   ", terminator: "");continue}
            if (i>=7) && (j>=7) {break outer}
            
            print(" \(i2c.isReachable(i<<4 + j) ? " x" : " ." )", terminator: "")
        }
        print()
    }
    print("\n")
    print("Is Reachable: \(i2c.isReachable(0x40))")
    

    This is what I get when running the code above:

    ➜  Example git:(master) ✗ swift run
    Compile Swift Module 'SwiftySHT20Example' (1 sources)
    Linking ./.build/armv7-unknown-linux-gnueabihf/debug/SwiftySHT20Example
    Detecting devices on the I2C bus:
    
        0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00           .  .  .  .  .  .  .  .  .  .  .  .  .
    10  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    20  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    30  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    40  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    50  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    60  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
    70  .  .  .  .  .  .  .
    
    Is Reachable: false
    

    Am I missing some kind of configuration? Or does it seem like a bug?

    opened by samco182 12
  • Unresolved gnu_dev_makedev on Debian Buster

    Unresolved gnu_dev_makedev on Debian Buster

    Board Type

    RPi 3 B+

    Operating System

    Raspian Buster

    Swift Version

    prebuilt swift 5.0.1v0.3 from buildSwiftOnArm

    Description

    building a small project that depends on MicroExpress and SwiftyGPIO

    The build error is

    /home/pi/workspace/mod_fluid_control/.build/checkouts/SwiftyGPIO/Sources/Mailbox.swift:179:42: error: use of unresolved identifier 'gnu_dev_makedev'
            if mknod(filename, S_IFCHR|0600, gnu_dev_makedev(100, 0)) < 0 {
                                             ^~~~~~~~~~~~~~~
    

    Some googling indicates it is part of glibc? I'm not sure.

    opened by jhoughjr 9
  • GPIO Inputs fail to execute `onChange`, `onFalling`, and `on Raising` closures

    GPIO Inputs fail to execute `onChange`, `onFalling`, and `on Raising` closures

    Board Type

    Raspberry Pi 3B+

    Operating System

    Raspbian

    Swift Version

    Swift 5.0.2, prebuilt binary from https://github.com/uraimo/buildSwiftOnARM/releases/tag/5.0.2

    Description

    I have connected a push button to the Raspberry Pi with the following schematic: image (this image was just for reference, I have connected my push button to Physical Pin 11 instead of Physical Pin 10)

    The code setup is the following:

    import Foundation
    import SwiftyGPIO
    
    let gpios = SwiftyGPIO.GPIOs(for: .RaspberryPi3)
    let realGPIO = gpios[.P17]!
    realGPIO.direction = .IN
    realGPIO.pull = .down
    realGPIO.bounceTime = 0.5
    
    print("GPIO STATE: \(realGPIO.value)")
    
    realGPIO.onFalling { _ in
        print("Falling activated")
    }
    
    RunLoop.main.run()
    

    Now, here is the problem: If I reboot my rasp, and immediately after reboot, I run my program, the onFailling closure (or any of the other available closures) never gets executed. Now, if I quit the program, and run it again (without rebooting) then everything works fine, the closure is correctly called after each push. And this behavior happens every time I reboot the rasp, during the first program run the closure never works, and from the second and on, it works perfectly.

    I've tried to follow the code to check where it gets stuck, and I've found out that only after reboot the code will get stuck on SwiftyGPIO.swift, makeInterruptThread() function on line 247. It never gets out of the poll function call. From the second program run to n run, then it does not get stuck in poll function since it always returns a 1 on button push.

    opened by samco182 8
  • Does not appear to work with the new Raspberry Pi Zero W

    Does not appear to work with the new Raspberry Pi Zero W

    Is there something special that needs to happen in order to use SwiftyGPIO with the new Raspberry Pi Zero W? I have swift 3.0.2 installed and working (I was able to build and run some existing apps that do not use SwiftyGPIO). And, I am going by the RPi Zero GPIO Header chart and have an LED hooked up to GPIO #3. Also tested that it works with the command line gpio program, and was able to get the LED to power on/off by setting GPIO pin 3 to high/low.

    I'm using the Examples/Basic/raspi1PlusAndZero/Sources/main.swift file (had to change the .RaspberryPiRevPlusZero to .RaspberryPiPlusZero) But when I build and run the program (it does run since I put some debug print statements in to confirm) I don't see a flashing LED and I think I should.

    opened by stulevine 8
  • Question about i2c

    Question about i2c

    https://github.com/uraimo/SwiftyGPIO/blob/c59c2ce8416137533cc0bd98ca8e4594fa09b990/Sources/I2C.swift#L335

    Perhaps it's because I'm tired and it's very late but I couldn't get past this line when reading the code. What is the purpose of the right-shift? Doesn't it always result in zero?

    opened by anohren 7
  • Error return value not checked in export

    Error return value not checked in export

    From strace:

    open("/sys/class/gpio//export", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 futex(0x76f62828, FUTEX_WAKE_PRIVATE, 2147483647) = 0 fstat64(3, {st_mode=S_IFREG|0770, st_size=4096, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x74d8a000 write(3, "2", 1) = -1 EBUSY (Device or resource busy) close(3) = 0 munmap(0x74d8a000, 4096) = 0

    bug 
    opened by hpux735 6
  • GPIO19 missing for RaspberryPi4

    GPIO19 missing for RaspberryPi4

    Board Type

    RaspberryPi4

    Operating System

    Raspbian

    Swift Version

    Swift version 5.1.5 (swift-5.1.5-RELEASE)

    Description

    GPIO19 for RaspberryPi4 is not included in GPIORPI4

    opened by simon2204 5
  • Fatal error: Parameter cannot be read

    Fatal error: Parameter cannot be read

    Board Type

    Raspberry Pi Model 3B v1.2

    Operating System

    Raspbian Buster

    (everything up-to-date)

    Swift Version

    Swift 5.0.2 with the pre-built binaries

    Description

    I'm just trying to execute

    sudo ./.build/debug/Projects

    the sample code on the README:

    import SwiftyGPIO
    
    let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi3)
    print(gpios)
    var gp = gpios[.P2]!
    
    print(gp)
    

    and I'm getting the following error:

    Fatal error: Parameter cannot be read.: file /home/archie/Swift/Projects/.build/checkouts/SwiftyGPIO/Sources/SwiftyGPIO.swift, line 334
    Current stack trace:
    [1]    21475 illegal hardware instruction  sudo ./.build/debug/Projects
    

    However, I don't get why I'm facing this error.

    Any ideas? Thanks.

    opened by cansurmeli 5
  • Library should throw errors rather than call fatalError

    Library should throw errors rather than call fatalError

    I'm running into an issue where the library calls fatalError, killing my program rather than throwing an Error for me to handle. By throwing an Error, you allow the client to determine how best to deal with problems. In my case, I can continue with limited functionality even though I can't access GPIOs.

    opened by JetForMe 0
  • 64bit PWM broken

    64bit PWM broken

    Board Type

    RaspberryPi 4 4GB

    Operating System

    Raspian Bullseye (64bit)

    Swift Version

    5.6.1 Release (Official, aarch64-unknown-linux-gnu)

    Description

    Using PWM results in a fatalerror, Fatal error: ioctl on mailbox failed!. This happens for all PWMs Looking into the code, it appears IOCTL_MBOX_PROPERTY is wrong. Its currently hard coded to 0xc0046400

     private let IOCTL_MBOX_PROPERTY: UInt = 0xc0046400 // _IOWR(100, 0, char *)
    

    From the C version they call _IOWR(100, 0, char *) which you have commented out, which results in avalue of 0xc0086400 since char * size is different.

    Changing the hard coded value works**, it gets past the fatal error just fine, Heres some code to have it be generic regardless of OS

    // ioctl helpers?
    fileprivate enum IOCTL {
      static let IOCPARM_MASK: UInt16 =  0x1fff
      static let IOC_VOID: UInt32 = 0x20000000
      static let IOC_OUT: UInt32 = 0x40000000
      static let IOC_IN: UInt32 = 0x80000000
      static let IOC_INOUT: UInt32 = IOC_IN | IOC_OUT
      static let IOC_DIRMASK: UInt32 = 0xe0000000
    
      static func _IOC (_ io: UInt32, _ group: UInt32, _ num: UInt32, _ len: UInt32) -> UInt32 {
        let rv = io | (( len & UInt32(IOCPARM_MASK)) << 16) | ((group << 8) | num)
        return rv
      }
      static func _IOWR (_ group: UInt32, _ num: UInt32, _ size: UInt32) -> UInt32 {
        return _IOC(IOC_INOUT, group, num, size)
      }
    }
    ...
        private let IOCTL_MBOX_PROPERTY: UInt =  UInt(IOCTL._IOWR(100, 0, UInt32(MemoryLayout<Int>.size)))
    

    However, with this now fixed, it's now crashing with bus error, specifically crashing around PWM.swift#414 where its trying to copy data.
    This is where I've hit a roadblock. Not sure what to do to keep debugging.
    Currently, i'm looking to use the rpi-ws281x c library directly in Swift as an alternative for my current project, as I've tested and it works just fine with Python bindings. Would love to have a pure swift soln working in 64bit OS's though

    opened by apocolipse 0
  • Is there any need for a remote Swift/GPIO implementation?

    Is there any need for a remote Swift/GPIO implementation?

    I was thinking it would be pretty straightforward to wrap SwiftyGPIO using my networking framework (https://github.com/robreuss/ElementalController) and enable developers to interact with RPi hardware remotely. The networking framework is fairly performant so it might capture a wide range of use-cases.

    I was curious to know if you thought there would much interest in it. I don't have a particular project in mind for myself so developing it would be entirely for the community, but I don't want to put the effort in if most Swift developers would simply interface to pigpio.

    opened by robreuss 0
  • One wire timing

    One wire timing

    Board Type

    Raspberry Pi4

    Operating System

    Raspbian

    Swift Version

    version 5.3.3

    Description

    I have a number of 1-wire sensor (as well as a 10 channel GPIO relay board), the issue is, I read the unwire temps every few minutes I call the getsalves function, and it looks like between the time I get the devices and then read them they are not in the w1/devices directory, I am guessing they are being updated.

    checkOneWireTemps <- my function

    mySensors = ["28-3c01b607c42c", "28-3c01b607e11a", "28-3c01b6070726", "28-3c01b607bee2", "28-3c01b607e2e6", "28-3c01b607ad96", "28-3c01b6070ed6", "28-3c01b607b037", "28-3c01b607dbb6", "28-3c01b607a9f6", "28-3c01b607b0cf"]

    and as I iterate through them:

    x = 28-3c01b607c42c y = ["6e 02 55 05 7f a5 a5 66 6d : crc=6d YES", "6e 02 55 05 7f a5 a5 66 6d t=38875"] insert into onewiretemps values ( now(), '28-3c01b607c42c', '38.875' ) x = 28-3c01b607e11a y = ["30 02 55 05 7f a5 a5 66 c9 : crc=c9 YES", "30 02 55 05 7f a5 a5 66 c9 t=35000"] insert into onewiretemps values ( now(), '28-3c01b607e11a', '35.0' ) x = 28-3c01b6070726 y = ["5b 02 55 05 7f a5 a5 66 c7 : crc=c7 YES", "5b 02 55 05 7f a5 a5 66 c7 t=37687"] insert into onewiretemps values ( now(), '28-3c01b6070726', '37.687' ) x = 28-3c01b607bee2 y = ["90 01 55 05 7f a5 a5 66 b4 : crc=b4 YES", "90 01 55 05 7f a5 a5 66 b4 t=25000"] insert into onewiretemps values ( now(), '28-3c01b607bee2', '25.0' ) x = 28-3c01b607e2e6 y = ["3b 02 55 05 7f a5 a5 66 26 : crc=26 YES", "3b 02 55 05 7f a5 a5 66 26 t=35687"] insert into onewiretemps values ( now(), '28-3c01b607e2e6', '35.687' ) x = 28-3c01b607ad96 y = ["43 02 55 05 7f a5 a5 66 b9 : crc=b9 YES", "43 02 55 05 7f a5 a5 66 b9 t=36187"] insert into onewiretemps values ( now(), '28-3c01b607ad96', '36.187' ) x = 28-3c01b6070ed6 y = ["40 02 00 00 1f 00 00 00 91 : crc=91 YES", "40 02 00 00 1f 00 00 00 91 t=36000"] insert into onewiretemps values ( now(), '28-3c01b6070ed6', '36.0' ) x = 28-3c01b607b037 Couldn't open 1-Wire device: /sys/bus/w1/devices/28-3c01b607b037/w1_slave: No such file or directory Aborted

    and seconds later :

    [email protected]:~/Developer/myHome2 $ l /sys/bus/w1/devices/ total 0 lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b6070726 -> ../../../devices/w1_bus_master1/28-3c01b6070726/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b6070ed6 -> ../../../devices/w1_bus_master1/28-3c01b6070ed6/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b6076217 -> ../../../devices/w1_bus_master1/28-3c01b6076217/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b6079c5e -> ../../../devices/w1_bus_master1/28-3c01b6079c5e/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607a9f6 -> ../../../devices/w1_bus_master1/28-3c01b607a9f6/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b607ad96 -> ../../../devices/w1_bus_master1/28-3c01b607ad96/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607b037 -> ../../../devices/w1_bus_master1/28-3c01b607b037/ lrwxrwxrwx 1 root 0 Jan 20 08:48 28-3c01b607b0cf -> ../../../devices/w1_bus_master1/28-3c01b607b0cf/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607b7b5 -> ../../../devices/w1_bus_master1/28-3c01b607b7b5/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607bac1 -> ../../../devices/w1_bus_master1/28-3c01b607bac1/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607bed9 -> ../../../devices/w1_bus_master1/28-3c01b607bed9/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b607bee2 -> ../../../devices/w1_bus_master1/28-3c01b607bee2/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607c18d -> ../../../devices/w1_bus_master1/28-3c01b607c18d/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b607c42c -> ../../../devices/w1_bus_master1/28-3c01b607c42c/ lrwxrwxrwx 1 root 0 Jan 20 08:46 28-3c01b607dbb6 -> ../../../devices/w1_bus_master1/28-3c01b607dbb6/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b607e11a -> ../../../devices/w1_bus_master1/28-3c01b607e11a/ lrwxrwxrwx 1 root 0 Jan 20 08:42 28-3c01b607e2e6 -> ../../../devices/w1_bus_master1/28-3c01b607e2e6/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607ed43 -> ../../../devices/w1_bus_master1/28-3c01b607ed43/ lrwxrwxrwx 1 root 0 Jan 20 08:47 28-3c01b607fcfb -> ../../../devices/w1_bus_master1/28-3c01b607fcfb/ lrwxrwxrwx 1 root 0 Jan 20 08:34 w1_bus_master1 -> ../../../devices/w1_bus_master1/

    I think you need add a catch when opening the file, just in case it doesn't exist at the time of reading.

    In the meantime, I'll just use the file system to read them.

    Thanks for everything else using the lib to control/check the relays.

    j.

    opened by jeff-at-theranch 0
  • Segmentation Fault

    Segmentation Fault

    Board Type

    RaspberryP4

    Operating System

    raspIO Bullseye 64 bit

    Swift Version

    5.5

    Description

    When using onRaising to measure the rotational speed of a 5000 rpm fan, I'm getting segmentation faults

    opened by ea7kir 0
  • How to read I2C data without sending a command?

    How to read I2C data without sending a command?

    Raspberry Pi 4B, RASPIO 64 bit Bullseye Lite, Swift Version 5.5

    For example, to read the Status Register of an SHT31 (datasheet page 13)

    Screenshot 2021-12-06 at 15 09 56

    would require...

    i2c.writeByte(0x44, command: 0xF3, value: 0x2D)
    let data = i2c.readData(0x44) // would return 6 bytes, but this is not possible.
    

    It would also be helpful to be able to send word commands like this...

    i2c.writeWord(0x44, command: 0xF32D)
    

    Any help with this would be most helpful, because I'm completely stuck.

    opened by ea7kir 26
Owner
uraimo
uraimo
Custom MacBook login screen and pam modules using multipeer connectivity and usb hardware checks with iOS app for sign in.

Custom MacBook login screen and pam modules using multipeer connectivity and usb hardware checks with iOS app for sign in.

null 2 Aug 17, 2022
Docker images for Swift on Raspberry Pi and other ARM devices from balena's base images.

Swift on Balena Welcome to Swift on Balena – a set of Docker images for Swift on Raspberry Pi and other ARM devices. These images are based on balena'

Will Lisac 172 Aug 26, 2022
A ARM macOS Virtual Machine, using macOS 12's new Virtualization framework.

macOS Virtual Machine A ARM macOS Virtual Machine, using macOS 12's new Virtualization framework. I copied KhaosT's code from here, all I did is chang

Ming Chang 123 Aug 13, 2022
A Swift cross-platform (Apple and Linux) networking library.

KippleNetworking A Swift library that offers cross-platform (Apple and Linux) networking support, intended for the creation of cross-platform SDKs to

Kipple 11 Sep 20, 2022
A GUI based virtualisation tool for running Linux on macOS Big Sur (x86 or arm64)

Project Mendacius GUI based virtualization tool to run Linux, based on the Virtualization framework introduced by Apple for macOS Big Sur with support

Praneet 111 Sep 1, 2022
Joplin - an open source note taking and to-do application with synchronization capabilities for Windows, macOS, Linux, Android and iOS. Forum: https://discourse.joplinapp.org/

Joplin® is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are sea

Laurent 32.1k Sep 17, 2022
Running GUI Linux in a virtual machine on a Mac

Running GUI Linux in a virtual machine on a Mac Install and run GUI Linux in a virtual machine using the Virtualization framework. การ build และใช้งาน

MrChoke 3 Jul 12, 2022
Easily generate cross platform Swift framework projects from the command line

SwiftPlate Easily generate cross platform Swift framework projects from the command line. SwiftPlate will generate Xcode projects for you in seconds,

John Sundell 1.8k Sep 14, 2022
Use this template as a starting point for any Swift 5 module that you want other people to include in their projects

Swift 5 Module Template Use this template as a starting point for any Swift 5 mo

James Knipe 0 Dec 28, 2021
A command line tool for managing Swift Playground projects on your Mac.

swift-playground-tools A command line tool for managing Swift Playground projects on your Mac. Generate Xcode Project $ playground-tools generate-xcod

Liam Nichols 0 Dec 31, 2021
OpenAPI specification generator for Vapor based Swift projects.

VaporToOpenAPI VaporToOpenAPI is a Swift library which can generate output compatible with OpenAPI version 3.0.1 from Vapor code. You can use generate

null 2 Aug 31, 2022
🚀 Create, maintain, and interact with Xcode projects at scale

What's Tuist ?? Tuist is a command line tool that helps you generate, maintain and interact with Xcode projects. It's open source and written in Swift

Tuist 2.9k Sep 19, 2022
📝 Read, update and write your Xcode projects

XcodeProj XcodeProj is a library written in Swift for parsing and working with Xcode projects. It's heavily inspired by CocoaPods XcodeProj and xcode.

Tuist 1.7k Sep 20, 2022
ConfettiKit is a custom framework used to add Confetti on your iOS/iPadOS projects.

ConfettiKit is a custom framework used to add Confetti on your iOS/iPadOS projects. The kit provides variety of customisations inorder to design a confetti which matches your project's UI. ConfettiKit makes your work of adding Confetti on your project with just one line of code.

Gokul Nair 13 Sep 15, 2022
Challenging each other to complete pet projects!

Podlodka Pet Project Challenge Мотивируем друг друга на завершение своих пет проджектов! Каждую неделю каждый участник вносит в банк 1 ставку и ведет

Vladimir Korolev 2 Aug 27, 2022
Ios jetpack - A codabase for iOS projects foundations

iOSJetpack A codabase for iOS projects foundations Neworking Data Reusable Proto

MonsterTechStudio 1 Jan 24, 2022
SwiftUITemplate - Template repository for SwiftUI projects

SwiftUITemplate Template repository for SwiftUI projects. Environment Name Versi

y-okudera 2 Jul 10, 2022
Save development time! Respresso automatically transforms and delivers your digital assets into your projects

Introduction Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import the latest

Respresso 8 Jan 28, 2022
Save development time! Respresso automatically transforms and delivers your digital assets into your projects

Respresso Android client Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import

Respresso 11 May 27, 2021