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 1.0.0
Swift wrapper for the Ink narrative scripting language. Based on InkJS. Requires JavaScriptCore (so no Linux support).
⭐️ 7
🕓 3 weeks ago
iOS macOS tvOS
.package(url: "https://github.com/maartene/InkSwift.git", from: "1.0.0")

InkSwift

Swift wrapper for the Ink narrative scripting language. Based on InkJS. This version requires JavaScriptCore (so for now no Linux support).

Supported features

  • 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);
  • Combine integration (subscribe to state changes, observe variables).

Limitations

  • This version uses JavaScriptCore. The JXKit branch has experimental support for Apple and Linux platforms using JXKit (https://github.com/jectivex/JXKit). The tests pass on macOS and Linux. For now I'll keep the two branches seperate, but I will probably merge them in due time.

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: [
        .macOS(.v10_15),    
    ],
    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: "0.0.2")
    ],
    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.
        .target(
            name: "test_swiftpm",
            dependencies: ["InkSwift"]),
        .testTarget(
            name: "test_swiftpmTests",
            dependencies: ["test_swiftpm"]),
    ]
)

Usage

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
    print(story.currentText)
    
    // If you can continue the story, we wait for input before continuing.
    if story.canContinue {
        print("Press 'enter' to continue")
        _ = readLine()
        story.continueStory()
    }
    // 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
                story.chooseChoiceIndex(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

Add

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 {
        Text(story.currentText)
        if story.canContinue {
            Button("Continue") {
                story.continueStory()
            }
        }
        ForEach(story.options, id: \.index) { option in
            Button(option.text) {
                story.chooseChoiceIndex(option.index)
            }
        }
    }.padding()
    .onAppear {
        loadStory()
    }
}

Licenced content

  • The Ink runtime uses the official Ink Javascript port InkJS

GitHub

link
Stars: 7
Last commit: 3 weeks ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Release Notes

Compiler support
3 weeks ago

InkSwift was stable for use for some time. But now InkJS has been updated to version 2.2.2. The compiler is now included, so you can load stories directly from Ink source code. Without needing to compile.

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