Building a Flashcards App for Studying Swift Syntax
My Swift notes used to contain plenty of copied code, yet I often struggled to recall even basic syntax when the Xcode editor was closed. That gap between recognising code and writing it prompted me to build SwiftSyntaxCards, a focused iOS app for studying Swift syntax. Instead of treating it as a general revision tool, I designed every feature around one habit: recalling a language construct quickly and correctly.
The app uses SwiftUI, Swift’s Codable protocol, and a lightweight spaced-repetition system. It includes cards for optionals, closures, protocols, pattern matching, access control, and common compiler errors. A useful learning app also needs clear progress tracking and dependable local storage, so I kept the first version offline-first and concentrated on a smooth daily review routine.
Make each card test one Swift idea
A card should ask for one retrievable fact rather than a broad explanation. “What does a closure capture?” is difficult because its answer can involve several concepts. “What keyword introduces an escaping closure parameter?” has a precise answer that can be remembered and tested.
For example, the front of one card displays guard let user else { return }. The back explains that guard let unwraps an optional, exits the current scope when the value is missing, and makes the unwrapped value available afterward. This format preserves the syntax while connecting it to its behaviour.
I also record why an answer is wrong. A card about some and any might compare their roles in return types, while another might highlight a compiler error caused by mixing them up. This turns incorrect answers into useful revision material instead of treating them as failures.
Model cards around spaced repetition
Each card needs more than its prompt and explanation. The data model stores the category, review history, current interval, ease factor, and next due date. A typed category also makes it possible to filter a study session around a weak area.
struct Flashcard: Identifiable, Codable {
let id: UUID
let category: SyntaxCategory
let prompt: String
let explanation: String
var dueDate: Date
var intervalDays: Int
var ease: Double
var repetitions: Int
}
The review scheduler responds to four ratings: Again, Hard, Good, and Easy. Again returns the card to the current session, while successful reviews increase its interval. The ease factor adjusts the growth rate for cards that are consistently easy or difficult.
enum Rating {
case again, hard, good, easy
}
func updatedCard(
from card: Flashcard,
rated rating: Rating,
at date: Date = Date()
) -> Flashcard {
var result = card
switch rating {
case .again:
result.intervalDays = 0
result.repetitions = 0
result.ease = max(1.3, card.ease - 0.2)
case .hard:
result.intervalDays = max(1, Int(Double(card.intervalDays) * 1.2))
result.repetitions += 1
result.ease = max(1.3, card.ease - 0.15)
case .good:
result.intervalDays = card.intervalDays == 0 ? 1 :
card.intervalDays == 1 ? 3 :
Int(Double(card.intervalDays) * card.ease)
result.repetitions += 1
case .easy:
result.intervalDays = max(2, Int(Double(card.intervalDays) * card.ease * 1.3))
result.repetitions += 1
result.ease = min(3.0, card.ease + 0.15)
}
result.dueDate = date.addingTimeInterval(
TimeInterval(result.intervalDays * 86_400)
)
return result
}
This is not a complete implementation of SM-2, but it provides predictable intervals without making the app difficult to test. Each scheduling decision can be checked with a fixed date, which removes today’s date from the expected results.
Keep the review flow quick and touch friendly
Opening the app should lead directly to a due card. The learner reads the prompt, taps to reveal the explanation, and selects a rating. There is no need to navigate through several screens between questions, because breaking the flow makes short practice sessions less likely.
SwiftUI works well for this structure with a NavigationStack, a state-driven study view model, and four clearly labelled rating buttons. Haptic feedback can confirm a selection, while Dynamic Type and generous spacing keep the controls usable for different accessibility needs. A UIKit implementation could achieve the same result with a collection view and diffable data source, but combining both frameworks in the first version would add unnecessary complexity.
This matters in places such as Melbourne and Sydney, where a developer might review a few cards while waiting for a tram or train. The interface is designed for one-handed use, places the rating controls within thumb reach, and avoids requiring precise gestures. Casual Australian wording keeps the experience friendly: a completion message can say “Nice work” or “No worries, keep going” without filling the app with forced slang.
Store decks locally and keep them portable
SwiftSyntaxCards saves its decks as JSON inside Application Support. This location suits structured files that should not appear among the user’s general documents. Encoding the complete deck also makes it straightforward to create backups, move cards between devices, or generate a fresh deck when the core syntax collection changes.
Each file includes a schema version. When the format changes, the decoder can migrate older data instead of discarding a learner’s review history. Writes happen through a temporary file that replaces the previous version only after encoding succeeds, reducing the chance of losing progress during a crash.
Cloud sync is deliberately absent from the initial release. A local-first design supports offline study, avoids account friction, and keeps the privacy story straightforward. A GitHub-backed import can be added later without changing the card model, provided imported cards receive new identifiers and begin with fresh scheduling data.
Turn every answer into useful feedback
The review screen shows the syntax again beside the explanation after each answer. This is important because recognising the correct keyword is only half the task; the learner should also understand where it belongs. A missed card can be reviewed immediately, but the scheduler still records the attempt so weak areas appear in the statistics.
The app tracks accuracy by category rather than displaying only a streak. This reveals practical gaps, such as repeatedly confusing map and flatMap or forgetting the difference between throws and rethrows. The dashboard can then recommend a short session containing those categories.
A typing mode adds another layer of retrieval. The learner types guard let rather than selecting it from multiple choices. The app compares the response after normalising whitespace and ignores capitalisation only where Swift permits it. It does not execute the submitted code; the goal is syntax recall, not building an online compiler.
Test scheduling, persistence, and the interface
The scheduling logic belongs in unit tests because small mistakes can create unreasonable review intervals. Tests use fixed dates to verify that an Again card returns immediately, a Good card advances by one day, and later successful reviews multiply the existing interval by the ease factor. Boundary tests cover zero intervals, the minimum ease value, and the maximum ease value.
Persistence tests encode a sample deck, write it to a temporary directory, decode it again, and compare the result. These tests protect future migrations and catch errors caused by changing a property without updating its coding configuration. Xcode’s test targets keep this process quick enough to run before every commit.
Interface tests cover the complete path from opening a deck to revealing an answer and recording a rating. They also check Dynamic Type, VoiceOver labels, dark mode, and empty states. Running the app on a physical device remains valuable because the simulator cannot reproduce every keyboard, haptic, or device-size behaviour.
Australian users expect dates such as 24/06/2025, so the interface uses the day-month-year order rather than an American format. Due dates are stored as absolute dates and converted through Calendar, allowing a learner in Perth to travel to Sydney without a fixed UTC offset producing incorrect review times.
Localise the experience for Australian learners
The first release uses Australian English, including spellings such as “localised” and “organisation”. A study reminder can mention an “arvo” review, but slang appears sparingly and never replaces clear instructions. The tone should feel natural without making international users decode unfamiliar language.
Pricing also needs to suit the Australian market. A paid syntax pack can be offered in Australian dollars through App Store Connect, with Apple handling the applicable 10% GST shown to Australian customers. The app should not calculate tax itself or alter a StoreKit price manually.
Screenshots should show real Swift syntax, readable typography, and the complete review interaction rather than decorative mockups. The App Store description can explain how cards are organised and how reviews are scheduled, while the privacy label remains accurate about locally stored learning data.
The practical takeaway is to keep the core loop small: one atomic card, one precise explanation, one rating, and one saved due date. Those parts already create a dependable study tool, while polished animations and cloud features can wait until the learning routine proves its value.