Swiftpack.co - maartene/InkSwift as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by maartene.
maartene/InkSwift 2.0.0
Swift wrapper for the Ink narrative scripting language. Based on InkJS. Requires JavaScriptCore (so no Linux support).
⭐️ 7
🕓 4 weeks ago
iOS macOS tvOS
.package(url: "https://github.com/maartene/InkSwift.git", from: "2.0.0")


Swift wrapper for the Ink narrative scripting language. Based on InkJS.

Supported features

  • Apple and Linux platforms (using JXKit). If you want to use the JavaScriptCore version (Apple platforms only), use release 0.0.4.
  • Loading compiled Ink stories loadStory(json: String) as well as Ink directly loadStory(ink: String);
  • Basic flow: continue story continueStory() and choices chooseChoiceIndex(_ index: Int);
  • Moving to knots/stitches moveToKnitStitch(_ knot: String, stitch: String? = nil);
  • Tag support. Read currentTags variable;
  • Setting and getting variable values (supports strings, 32-bit integers and doubles);
  • Loading and saving state stateToJSON() and loadState(_ jsonDataString: String);
  • On Apple platforms: Combine integration (subscribe to state changes, observe variables).

Linux installations

JXKit on Linux requires JavaScriptCore libraries, that are part of WebKit. For Ubuntu 20.04 and newer: sudo apt install libwebkit2gtk-4.0-dev


  • When using the built in Ink compiler, don't use JavaScript tokens that can confuse string definitions, like `.

Getting started

Regular XCode project

Use 'File' -> 'Swift Packages' -> 'Add Package Dependency...' to add the package to your project.

Using SwiftPM

Add InkSwift as a dependency to Package.swift:

let package = Package(
    name: "test_swiftpm",   // choose your own name
    platforms: [
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/maartene/InkSwift.git", from: "1.0.0")
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
            name: "test_swiftpm",
            dependencies: ["InkSwift"]),
            name: "test_swiftpmTests",
            dependencies: ["test_swiftpm"]),


Start by creating a InkStory

let story = InkStory()

Then load a story from a Ink JSON (you can use Inklecate or Inky to convert an .ink file to a .json file.):

let storyJSON = ... //
story.loadStory(json: storyJSON)

Alternatively, you can load a story from Ink directly. Not however, that this might hinder performance for users.

story.loadStory(ink: "Hello, World!")

You can create a very basic command line 'player' using just a few lines of code:

// As long as the story can continue (either because there is more text or there are options you can choose)), keep the loop going
while story.canContinue || story.options.count > 0 {
    // Print the current text to the console/terminal

    // If you can continue the story, we wait for input before continuing.
    if story.canContinue {
        print("Press 'enter' to continue")
        _ = readLine()
    // If there are options, show the options and wait for player to choose
    else if story.options.count > 0 {
        // print every option
        for option in story.options {
            print("\(option.index). \(option.text)")
        print("What is your choice?")

        // wait for input from player
        if let choice = readLine() {
            // try and convert input to an index.
            if let index = Int(String(choice.first ?? "a")) {
                // choose the selected option index
// no more content, story is done.
print("Story done!")

Using Combine/SwiftUI

InkStory conforms to the ObservableObject protocol. This makes using it in Combine possible and SwiftUI very easy. A simple example SwiftUI view that can play an Ink story would contain:

Import the InkSwift package


import InkSwift

to ContentView.swift

The ink story as a @StateObject

Add the following property to your ContentView: @StateObject var story = InkStory()

Add a function that loads the Ink story:

Note: change the filename to load to your own JSON file. Don't forget to add it to the project.

func loadStory() {
guard let url = Bundle.main.url(forResource: "test.ink", withExtension: "json") else {
    fatalError("Could not find ink story file.")

guard let storyJSON = try? String(contentsOf: url) else {
    fatalError("Could not load story file.")

story.loadStory(json: storyJSON)

Create the body property

var body: some View {
    VStack {
        if story.canContinue {
            Button("Continue") {
        ForEach(story.options, id: \.index) { option in
            Button(option.text) {
    .onAppear {

Licenced content

  • The Ink runtime uses the official Ink Javascript port InkJS
  • The Compiler tests use The Intercept as an example story to compile
  • Uses JSONEquality in some of the tests
  • Cross-platform JavaScript runtime JXKit


Stars: 7
Last commit: 4 weeks ago
Advertisement: IndiePitcher.com - Cold Email Software for Startups

Release Notes

Cross-platform support by default
24 weeks ago

JXKit is now the default JavaScript engine, providing cross-platform support for Apple and Linux platforms.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics