Announcing TinyString for Embedded Swift, Wasm, and the world

Hello friends!

I love Embedded Swift, but its lack of a mutable String-like type is a difficult limitation. So, I made a TinyString as a way to do some basic text manipulation for the ASCII character set.

The Problem

String manipulation is a common use case — even on embedded systems. But Swift’s String type is not available on Embedded Swift because it contains all the megabytes of Unicode goodness required for complete text handling of an OS.

Requirements:

  • Be Swifty
  • Safe
  • Aggressively lightweight
  • Work great with C-strings
  • Work great with Swift String
  • Work with many — hopefully all — supported Swift platforms
  • Zero runtime dependencies

Find TinyString on Swift Package Index

https://swiftpackageindex.com/mcritz/TinyString

omnimo

Use Cases

Parsing/building small ASCII strings at runtime - JSON fields, BLE tokens, sensor labels, LVGL UI, logging values, etc.

TinyString Pros

  • Swift superpowers
    • Ergonomic, expressive, and safe
      • ByteArray !? I barely knew her! TinyString provides a nice, String-like type for low-level text
      • Span support for bounds-checked safety
      • An optional throwing-strict init that validates input
      • Comes with useful protocols and extensions, and you can add your own
  • Can construct text at runtime
    • Unlike StaticString, which is only for compile-time literals
    • var name: TinyString = "Wai Yin"
      • Note: TinyString is ExpressibleByStringLiteral
  • Designed for Embedded Swift, Swift Wasm, AND all other platforms
    • ≈56 KB release build for the entire package, BUT my project use-cases build to ≈1 KB. Unlike String which is too large for embedded platforms
    • TinyString does not import Foundation
    • Designed to work safely with C-string
      • name.withCString { some_c_func($0) }
      • let cStringValue = sensor.value // UnsafePointer<CChar>
        let sensorValue = TinyString(cString: cStringValue) // replaces non-ASCII Chars with `?`
        // Or require a valid C string
        guard let confirmedValue = try? TinyString(strict: cStringValue) else { return } // disallows non-ASCII values
  • Has String-y features
    • Interpolation var greeting: TinyString = "Hello, \(name)!"
    • Mutation greeting.append("\n Welcome to Embedded Swift")
      • greeting += "!"
    • Collection/Equatable/Hashable
      • greeting.hasPrefix("Hello")
      • greeting.contains(name)
      • name.isAllLetters
      • and so on…
  • Works well with String on other platforms
    • let stringGreeting = String(greeting)
    • let stringName: String = name.description // TinyString is CustomStringConvertible
    • // On Apple platform to TinyString
      let bleCommand: String = bluetoothCommand(for: stringName)
      let command = TinyString(bleCommand)

TinyString Limitations

  • ASCII only
  • Full Release build: ≈56 KB (Embedded) / ≈169 KB (Wasm)
    • Note: release builds for your project will prune unused code. Actual compiled overhead may be much smaller.
  • TinyString’s internal storage is a heap-allocated object — know your tradeoffs!
    • Use InlineTinyString for stack-allocation
  • Doesn’t support StringProtocol
8 Likes