Docs · Apple
Apple quickstart
Add the Swift package to your app in Xcode, compile your first font on the device, and show it in SwiftUI. It takes about ten minutes.
The Swift SDK is the engine as a native framework, TypomatEngine. It runs inside your app, with no network use and no dependencies, so fonts are made on your users' iPhones, iPads and Macs.
1. Add the package
Swift Package Manager (SwiftPM) is the way Xcode adds libraries to an app. You give it the package's address, and it downloads and links it for you.
- In Xcode, open your project and choose File, then Add Package Dependencies.
- Paste
https://github.com/MarcoLuthi/typomat-engine-openinto the search field at the top right. - Leave the rule at Up to Next Major Version, from 1.7.1, and click Add Package.
- Tick your app's target next to TypomatEngine, and click Add Package again.
What you should see: TypomatEngine under Package Dependencies, in the navigator on the left. Start from 1.7.1 or later, as earlier versions aren't available.
What Xcode downloads is an xcframework: the compiled engine for every Apple platform, in one bundle, from this website. It's signed with an Apple Developer ID: Marco Luethi, team 5NUSWX8E44, which Xcode shows when you select it. To add it by hand instead, download the signed framework (10 MB).
If your code lives in a Swift package of its own, add it to that package's manifest instead:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "AcmeFonts",
platforms: [.iOS(.v17), .macOS(.v14), .macCatalyst(.v17)],
dependencies: [
.package(url: "https://github.com/MarcoLuthi/typomat-engine-open", from: "1.7.1"),
],
targets: [
.target(
name: "AcmeFonts",
dependencies: [.product(name: "TypomatEngine", package: "typomat-engine-open")]
),
]
)2. Make the engine
One TypomatEngine does everything. Make it once, when your app starts or when someone opens your font tool, and share it: it can be used from any thread.
import TypomatEngine
let engine = TypomatEngine(licenseKey: "TM2...")
print(engine.license.mode) // licensed, or evaluation
print(engine.license.reason?.rawValue ?? "none") // why not: missing, malformed, app, ...What you should see: licensed and none with a key that fits. Otherwise evaluation and a reason: missing with no key, malformed for the placeholder above, or app when your key doesn't name this app. The engine reads your app's bundle ID by itself, and checks the key on the device: nothing is sent anywhere.
3. Compile a font
A Recipe is the font's source: a name and a few parameters, numbers that set things like weight and contrast. Anything you leave out stays at its default. compile turns it into a whole font file.
let recipe = Recipe(name: "Acme Display", parameters: [
"weight": 118, "contrast": 0.92, "serif": 0.5, "xHeight": 470,
])
let font = try await engine.compile(recipe) // Data: a whole TrueType file
let file = URL.documentsDirectory.appending(path: "acme-display.ttf")
try font.write(to: file)
print(file.path, font.count)What you should see: the file's path and its size, about 54 KB with a licence key. Compiling takes a fraction of a second, away from the main thread, so it's safe to await from a view. That's TrueType: pass format: .woff2, for example, for another format.
4. Use it in SwiftUI
You don't have to install a font to use it. registerFont makes it available to your app by name, until the app quits or you call unregister().
import SwiftUI
import TypomatEngine
struct ContentView: View {
let engine: TypomatEngine
@State private var fontName: String?
var body: some View {
Text("Acme Display")
.font(fontName.map { .custom($0, size: 40) } ?? .largeTitle)
.task {
let recipe = Recipe(name: "Acme Display", parameters: ["weight": 118, "contrast": 0.92])
guard let data = try? await engine.compile(recipe),
let font = try? await TypomatEngine.registerFont(data) else { return }
fontName = font.postScriptName
}
}
}What you should see: the text in the system font, then in the new one a moment later. Registering a font under a name you've already used replaces it, but a SwiftUI view may keep showing the earlier one until it asks for the font again. To change a view at once, give each look its own recipe name, as Font controls in an app does.
Building a command-line tool?
The framework is dynamic: an app embeds it by itself, but a command-line tool has nowhere to put it. Ship TypomatEngine.framework beside the tool, and link with -rpath @loader_path so the tool finds it.