Using ChatGPT to Help Write SwiftUI Code in Streaks

In the next version of Streaks, we're adding the ability to record notes to tasks. As part of this update we wanted to include this in our "what's new" screen.

To give it a more realistic feel, I wanted this information to be typed in the same way notes are recorded in the app:

Animating the "what's new" text for the notes feature.

This particular screen uses SwiftUI, but off the top of my head, I wasn't sure of the easiest to create this typing effect. Instead of using Google or Stack Overflow, I thought this might be a good chance to try ChatGPT:

Write SwiftUI code that animates a string of text to look type it is being typed in real time

To which ChatGPT replied:

Here's an example of how to animate a string of text to look like it's being typed in real time using SwiftUI:
import SwiftUI

struct TypingText: View {
    @State private var text = ""
    let fullText: String
    let typingInterval: Double

    init(text: String, typingInterval: Double = 0.1) {
        self.fullText = text
        self.typingInterval = typingInterval
    }

    var body: some View {
        Text(text)
            .onAppear {
                self.typeText()
            }
    }

    func typeText() {
        var currentIndex = text.endIndex
        Timer.scheduledTimer(withTimeInterval: typingInterval, repeats: true) { timer in
            if currentIndex < self.fullText.endIndex {
                currentIndex = self.fullText.index(after: currentIndex)
                self.text = String(self.fullText[..<currentIndex])
            } else {
                timer.invalidate()
            }
        }
    }
}
In this example, TypingText takes in a text parameter, which is the full text that you want to animate, and an optional typingInterval parameter, which determines the interval at which the next character is added. The typeText() function uses a Timer to incrementally add characters to the text state property and update the view.

Great! That saved me a bunch of time. This is what it looks like when run:

The code as supplied by ChatGPT.

I wanted it to feel more realistic though, which would be by using different intervals between each letter.

Change it so the typing interval is randomised so it feels more real.

Right away, the response:

Here's an example of how to randomize the typing interval so that the animation feels more like someone typing the text in real time:
import SwiftUI

struct TypingText: View {
    @State private var text = ""
    let fullText: String
    let typingIntervalRange: ClosedRange<Double>

    init(text: String, typingIntervalRange: ClosedRange<Double> = 0.05...0.15) {
        self.fullText = text
        self.typingIntervalRange = typingIntervalRange
    }

    var body: some View {
        Text(text)
            .onAppear {
                self.typeText()
            }
    }

    func typeText() {
        var currentIndex = text.endIndex
        Timer.scheduledTimer(withTimeInterval: randomTypingInterval(), repeats: true) { timer in
            if currentIndex < self.fullText.endIndex {
                currentIndex = self.fullText.index(after: currentIndex)
                self.text = String(self.fullText[..<currentIndex])
                timer.invalidate()
                self.typeText()
            } else {
                timer.invalidate()
            }
        }
    }

    func randomTypingInterval() -> Double {
        let interval = Double.random(in: typingIntervalRange)
        return interval
    }
}
In this example, TypingText takes in a text parameter, which is the full text that you want to animate, and an optional typingIntervalRange parameter, which determines the range of possible typing intervals. The randomTypingInterval() function generates a random value within this range to use as the typing interval for each character. The typeText() function uses a Timer to incrementally add characters to the text state property and update the view. Each time the timer is invalidated and restarted, it uses a new random typing interval.

Here's what it looks like:

Randomised delays between each letter.

The change is subtle, but it feels a bit more realistic. You can easily adjust the range so the effect is more pronounced.

I didn't love how it generated the timers - seemed a bit wasteful to create a repeating timer, only to cancel it every time, so I made a few changes.

Here's the final version I wrote. This version includes a typingDelay parameter, so the typing doesn't begin until after that initial delay.

public struct TypingText: View {
    @State private var typedText = ""

    var text: String
    var typingSpeed: ClosedRange<TimeInterval>
    var typingDelay: TimeInterval

    public init(
        text: String,
        typingSpeed: ClosedRange<TimeInterval> = 0.1...0.2,
        typingDelay: TimeInterval = 0
    ) {
        self.text = text
        self.typingSpeed = typingSpeed
        self.typingDelay = typingDelay
    }

    public var body: some View {
        Text(typedText)
        .onAppear {
            if typingDelay > 0 {
                Timer.scheduledTimer(withTimeInterval: typingDelay, repeats: false) { _ in
                    self.startTyping(0)
                }
            }
            else {
                self.startTyping(0)
            }
        }
    }

    private func startTyping(_ index: Int) {
        Timer.scheduledTimer(
            withTimeInterval: TimeInterval.random(in: typingSpeed),
            repeats: false
        ) { timer in
            guard index < self.text.count else {
                return
            }

            self.typedText.append(
                self.text[self.text.index(self.text.startIndex, offsetBy: index)]
            )

            startTyping(index + 1)
        }
    }
}

This version of the code can be seen in the first animation in this article.

On balance, using ChatGPT was a very useful way to mock up some working code. While some tweaks were needed for my specific application, it was ultimately a huge time saver.