Skip to content

KevinVitale/SwiftSDL

Repository files navigation

SwiftSDL β€” Cross-Platform Targets with Swift & SDL3

license

SwiftSDL is an open-source Swift library that provides a complete interface for working with the C-based SDL (Simple DirectMedia Layer) library. This wrapper allows developers to leverage SDL's cross-platform multimedia and game development capabilities in Swift applications across macOS, iOS, and Linux.

SDL2 Simple DirectMedia Layer 3.0

"Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL/Direct3D/Metal/Vulkan. It is used by video playback software, emulators, and popular games including Valve's award winning catalog and many Humble Bundle games." - wiki.libsdl.org

🏁 Getting Started

πŸ“‹ Requirements

πŸ”§ Installation

// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "MySDLGame",
    platforms: [
      .macOS(.v13)
    ],
    dependencies: [
      .package(url: "https://github.com/KevinVitale/SwiftSDL.git", from: "0.2.0-alpha.16"),),
    ],
    targets: [
        .executableTarget(
            name: "SwiftSDLTest",
            dependencies: [
              "SwiftSDL"
            ],
            resources: [
              .process("../Resources/BMP"),
            ]
        ),
    ]
)

πŸ‘€ Overview

Like Swift itself, SwiftSDL makes SDL3 approachable for newcomers and powerful for experts.

πŸ‘Ύ Introduction

A complete SwiftSDL game consists of the following 22-lines of code:

import SwiftSDL

@main
final class MyGame: Game {
    private enum CodingKeys: String, CodingKey {
        case options
    }

    @OptionGroup
    var options: GameOptions

    func onReady(window: any Window) throws(SDL_Error) {
      /* create a renderer, or acquire a gpu device. */
      /* load assets or resources. */
    }

    func onUpdate(window: any Window, _ delta: Uint64) throws(SDL_Error) {
      /* handle game logic and render frames */
    }

    func onEvent(window: any Window, _ event: SDL_Event) throws(SDL_Error) {
      /* respond to events */
    }

    func onShutdown(window: (any Window)?) throws(SDL_Error) {
      /* release objects and unload resources */
    }
}

The class, MyGame, conforms to the Game protocol. A window is created automatically using reasonable defaults, although, it's possible to override the window's creation manually.

Underneath the hood, the Game protocol has implemented SDL3's new main callbacks.

GameOptions are runtime arguments which alter the behavior or your application, such as your window's appearance.

🧩 Tutorial

Let's create an app using SwiftSDL from start-to-finish.

Step 1: Create the project

Using Swift's command-line utility, we'll create the project in an empty directory

# Create an empty directory for our project
mkdir MyGame
cd MyGame

# Create the executable package
swift package init --type executable

Step 2: Add SwiftSDL

Update the Package.swift file to include SwiftSDL as a dependency:

Note: you may need adjust platforms to use .macOS(.v13).

// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "MyGame",
    platforms: [
        .macOS(.v13)
    ],
    dependencies: [
      .package(url: "https://github.com/KevinVitale/SwiftSDL.git", from: "0.2.0-alpha.17")
    ],
    targets: [
        // Targets can depend on other targets in this package and products from dependencies.
        .executableTarget(
            name: "MyGame",
            dependencies: [
               "SwiftSDL"
            ]
        ),
    ]
)

Step 3: Create the game

Rename main.swift to MyGame.swift:

mv Sources/main.swift Sources/MyGame.swift

Then replace MyGame.swift with the following code:

import SwiftSDL

@main
final class MyGame: Game {
    private enum CodingKeys: String, CodingKey {
      case options, message
    }

    @OptionGroup
    var options: GameOptions

    @Argument
    var message: String = "Hello, SwiftSDL!"

    private var renderer: (any Renderer)! = nil

    func onReady(window: any Window) throws(SDL_Error) {
      renderer = try window.createRenderer()
    }

    func onUpdate(window: any Window, _ delta: Uint64) throws(SDL_Error) {
      try renderer
        .clear(color: .gray)
        .debug(text: message, position: [12, 12], scale: [2, 2])
        .fill(rects: [24, 48, 128, 128], color: .white)
        .fill(rects: [36, 60, 104, 104], color: .green)
        .present()
    }

    func onEvent(window: any Window, _ event: SDL_Event) throws(SDL_Error) {
    }

    func onShutdown(window: (any Window)?) throws(SDL_Error) {
      renderer = nil
    }
}

Then start the game:

swift run

Note: You should see a window with a gray background, a message saying "Hello, SwiftSDL!", and two squares: one large white one, and a smaller green one.

Your game has several options built-in. To see them all, use --help:

swift run MyGame --help
USAGE: my-game [<options>] [<message>]

ARGUMENTS:
  <message>               (default: Hello, SwiftSDL!)

OPTIONS:
  --hide-cursor           Hide the system's cursor
  --auto-scale-content    Stretch the content to fill the window
  --logical-size <logical-size>
                          Forces the rendered content to be a certain logical size (WxH)
  --logical-presentation <logical-presentation>
                          Forces the rendered content to be a certain logical order; overrides '--auto-scale-content' (values: disabled,
                          stretch, letterbox, overscan, integer-scale; default: disabled)
  --vsync-rate <vsync-rate>
                          Set vertical synchronization rate (values: adaptive, disabled, interger value; default: disabled)
  --window-always-on-top  Window is always kept on top
  --window-fullscreen     Window is set to fullscreen
  --window-transparent    Window is uses a transparent buffer
  --window-maximized      Create a maximized window; requires '--window-resizable'
  --window-minimized      Create a minimized window
  --window-max-size <window-max-size>
                          Specify the maximum window's size (WxH)
  --window-min-size <window-min-size>
                          Specify the minimum window's size (WxH)
  --window-mouse-focus    Force the window to have mouse focus
  --window-no-frame       Create a borderless window
  --window-resizable      Enable window resizability
  --window-position <window-position>
                          Specify the window's position (XxY)
  --window-size <window-size>
                          Specify the window's size (WxH)
  --window-title <window-title>
                          Specify the window's title
  -h, --help              Show help information.

Step 4: Sample Apps

SwiftSDL includes several samples to help you get started.

Test Bench

These are reimplementations of a variety of SDL3's tests using SwiftSDL:

Build Command Image Preview
swift run sdl test controller test-controller
swift run sdl test camera test-camera
swift run sdl test geometry test-geometry
swift run sdl test sprite test-sprite
Games
Build Command Image Preview
swift run sdl games flappy-bird game-flappy-bird
Xcode Project: macOS, iOS, tvOS

Explore: Samples/SwiftSDL-Xcode

πŸ’» Platform-Specific Instructions

SwiftSDL doesn't work without SDL3. Refer to the following sections to ensure SwiftSDL compiles properly.

Apple

SwiftSDL works on macOS, iOS, and tvOS simply by adding it to your project's Package.swift file. A precompiled XCFramework containing the SDL3 library is provided.

Building the XCFramework with the Makefile

You do not need to build the XCFramework yourself. However, if you need to, the available Makefile can be used:

# Clone KevinVitale/SwiftSDL
git clone https://github.com/KevinVitale/SwiftSDL
cd SwiftSDL

# Build XCFramework...grab some β˜•οΈ
make build-sdl-xcframework

macOS-example iOS-example

Linux

You must build and install SDL3 from source. Thankfully, it's easy and should take only a few minutes:

  1. Install whichever dependencies your need for your game; and,
  2. Build and install from source.

linux-example

Windows

Support for Windows is currently unavailable.

🎨 Authors

πŸ“ License

SwiftSDL is open-sourced under the MIT license. See the LICENSE file for details.