Kelvin Grid (beta)
Embed a fully native Wordle-style word-guessing puzzle into your mobile app with a single line of code.
KelvinGridView(model: .example)What's included
Quick integration
Embed a fully native word-guessing puzzle into your mobile app with a single line of code.
Customise grid & keyboard
Swap in your own grid cell and keyboard key views with a simple modifier on each.
Row & completion callbacks
React to every submitted guess and get notified when the game ends — win or lose.
The model
A KelvinGridModel holds the target word and the maximum number of allowed guesses. Pass in any word and attempt limit, or use .example to get started immediately.
struct KelvinGridModel {
let word: String // the target word to guess
let attempts: Int // maximum number of guesses allowed
static var example: KelvinGridModel { get }
}
// Create a model with your own word and attempt limit
let model = KelvinGridModel(word: "SWIFT", attempts: 6)Quick integration
Pass a puzzle model into the view to render a fully playable word-guessing grid — complete with a QWERTY keyboard — in a single line.
KelvinGridView(model: .example)Customise grid cells
Control the spacing between cells and swap in your own custom cell view.
KelvinGridView(model: .example)
.grid(spacing: 8, cell: KelvinGridCell.self)Your custom cell conforms to a protocol that requires the letter to display, the evaluation state — correct, misplaced, wrong with an alphabetical offset, or empty — and whether the cell belongs to the row currently being typed.
struct KelvinGridCell: View, KelvinGridCellProtocol {
var letter: String
var state: KelvinCellState
var isActiveRow: Bool
init(letter: String, state: KelvinCellState, isActiveRow: Bool) {
self.letter = letter
self.state = state
self.isActiveRow = isActiveRow
}
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 6)
.fill(backgroundColor)
if case .wrong(let offset) = state {
VStack(spacing: 1) {
Text(letter).font(.title2.bold()).foregroundStyle(.white)
Text(offset >= 0 ? "+\(offset)" : "\(offset)")
.font(.system(size: 9, weight: .semibold))
.foregroundStyle(.white.opacity(0.85))
}
} else {
Text(letter).font(.title2.bold()).foregroundStyle(foregroundColor)
}
}
}
}Customise keyboard keys
Replace the default QWERTY key buttons with your own custom key view.
KelvinGridView(model: .example)
.grid(spacing: 8, cell: KelvinGridCell.self)
.input(cell: KelvinKey.self)Your custom key conforms to a protocol that requires the key label — a single letter or the delete symbol — and a tap handler to call when the key is pressed.
struct KelvinKey: View, KelvinKeyProtocol {
var label: String
var onTap: () -> Void
init(label: String, onTap: @escaping () -> Void) {
self.label = label
self.onTap = onTap
}
var body: some View {
Button(action: onTap) {
Text(label)
.font(.subheadline.bold())
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(Color(.systemGray5))
.foregroundStyle(.primary)
.clipShape(RoundedRectangle(cornerRadius: 6))
}
}
}Row callbacks
React to every submitted row — receive the guessed word and the evaluation state for each letter position.
KelvinGridView(model: .example)
.grid(spacing: 8, cell: KelvinGridCell.self)
.input(cell: KelvinKey.self)
.onInput { guess, states in
print("Guessed: \(guess)")
}Completion callbacks
Get notified when the game ends — whether the player guessed the word correctly or used all their attempts.
KelvinGridView(model: .example)
.grid(spacing: 8, cell: KelvinGridCell.self)
.input(cell: KelvinKey.self)
.onInput { guess, states in
print("Guessed: \(guess)")
}
.onCompletion { didWin in
print(didWin ? "Correct!" : "Game over.")
}Explore the examples
Explore a fully working iOS and Android sample project you can run immediately.