Word Games

Word Wheel (beta)

Embed a fully native Word Wheel puzzle into your mobile app with a single line of code.

WordWheelView(model: .example)
    .grid(radius: 120)
Word Wheel
Word Wheel
Word Wheel

What's included

Quick integration

Embed a fully native Word Wheel puzzle into your mobile app with a single line of code.

Customise every piece

Replace the input display, letter tiles, and action buttons independently with your own views.

Word callbacks

React to every submitted word — validate it against your answers and track found words in your own state.

Control grid radius

Fix the distance from the centre letter to each surrounding tile with a single modifier, or let it scale automatically.


Quick integration

Pass a puzzle model into the view to render a fully playable Word Wheel — letter tiles and action buttons — in a single line.

WordWheelView(model: .example)

Control grid radius

Set the distance from the centre letter to each surrounding tile. When not set the radius scales automatically with the available space. Supply an explicit value to fix the spacing regardless of how much room the wheel is given.

WordWheelView(model: .example)
    .grid(radius: 120)

Customise input display

Replace the word-in-progress banner above the wheel with your own view.

WordWheelView(model: .example)
    .inputView(MyInputView.self)

Your custom view receives the word currently being built and the total number of letters on the wheel.

struct MyInputView: View, WordWheelInputViewProtocol {
    var word: String
    var isValid: Bool
    var letterCount: Int

    init(word: String, isValid: Bool, letterCount: Int) {
        self.word = word
        self.isValid = isValid
        self.letterCount = letterCount
    }

    var body: some View {
        HStack {
            Text(word.isEmpty ? "Start typing…" : word)
                .font(.title2.bold())
                .foregroundStyle(isValid ? .green : .primary)
                .frame(maxWidth: .infinity, alignment: .leading)
            Text("\(word.count) / \(letterCount)")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 12)
        .background(.ultraThinMaterial)
        .clipShape(RoundedRectangle(cornerRadius: 12))
    }
}

Customise letter tiles

Replace the circular letter tiles around the wheel and at the centre with your own view.

WordWheelView(model: .example)
    .input(cell: WordWheelInput.self)

Your custom tile conforms to a protocol that receives the letter, whether it is the centre tile, whether it has already been used in the current attempt, and a tap handler to call when tapped.

struct WordWheelInput: View, WordWheelInputProtocol {
    var letter: String
    var isMain: Bool
    var isUsed: Bool
    var onTap: () -> Void

    init(letter: String, isMain: Bool, isUsed: Bool, onTap: @escaping () -> Void) {
        self.letter = letter
        self.isMain = isMain
        self.isUsed = isUsed
        self.onTap = onTap
    }

    var body: some View {
        ZStack {
            Circle()
                .fill(isUsed ? .gray.opacity(0.4) : (isMain ? .indigo : .blue))
            Text(letter)
                .font(isMain ? .title2.bold() : .headline)
                .foregroundStyle(.white)
        }
        .onTapGesture {
            guard !isUsed else { return }
            onTap()
        }
    }
}

Customise action buttons

Replace the Submit, Delete, and Clear buttons below the wheel with your own view.

WordWheelView(model: .example)
    .actionButton(cell: WordWheelActionButton.self)

Your custom button conforms to a protocol that receives the button label and a tap handler. The same view is reused for all three actions — use the label to differentiate their appearance.

struct WordWheelActionButton: View, WordWheelActionButtonProtocol {
    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)
                .padding(.vertical, 12)
                .background(label == "Submit" ? .green : (label == "Delete" ? .orange : .red.opacity(0.8)))
                .foregroundStyle(.white)
                .clipShape(RoundedRectangle(cornerRadius: 10))
        }
    }
}

Word callbacks

React to every word the player submits — validate it against your answer list and track found words in your own state.

WordWheelView(model: .example)
    .input(cell: WordWheelInput.self)
    .actionButton(cell: WordWheelActionButton.self)
    .onWordSubmitted { word in
        if acceptableAnswers.contains(word) && !foundWords.contains(word) {
            foundWords.append(word)
        }
    }

Explore the examples

Focus

Explore a fully working iOS and Android sample project you can run immediately.