NOTE
Mr. Gordon generated this documentation using the Claude Anthropic large language model.
Overview
This Swift and SwiftUI app implements a “Choose Your Own Adventure” style interactive story experience. The app connects to a Supabase database to retrieve story content, track reader progress, and store user preferences. This document explains how the various components work together to create this interactive reading experience.
Data Models
Page
The Page struct represents a single page in the adventure book.
struct Page: Identifiable, Codable {
var id: Int // Unique identifier for the page
var narrative: String // The story text for this page
var image: String? // Optional image filename
var endingContext: String? // Optional context text if this page is an ending
var endingTypeId: Int? // Optional reference to ending type (if this page is an ending)
// Computed property to determine if this page is an ending to the story
var isAnEndingOfTheStory: Bool {
return endingTypeId != nil
}
}Edge
The Edge struct represents the connections between pages, essentially the choices a reader makes.
struct Edge: Identifiable, Codable {
var id: Int // Unique identifier for the edge
var prompt: String // The text shown to the reader as a choice
var fromPage: Int // The page this edge originates from
var toPage: Int // The page this edge leads to
}EndingType
The EndingType struct categorizes different types of story endings.
struct EndingType: Identifiable, Codable {
var id: Int // Unique identifier for the ending type
var label: String // Descriptive label for this ending type
var color: String // Color associated with this ending type
}Reader
The Reader struct stores information about the reader, including preferences and progress.
struct Reader: Identifiable, Codable {
var id: Int? // Unique identifier for the reader
var name: String? // Reader's name (optional)
var prefersDarkMode: Bool // Whether reader prefers dark mode
var lastPageReadId: Int? // ID of the last page read (for resuming)
}State Management
BookStore
The BookStore class manages the state of the book as it’s being read.
@Observable
class BookStore: Observable {
var reader: Reader // Stores reader preferences and state
var currentPageId: Int? // ID of the current page being viewed
var firstPageId: Int? // ID of the first page of the book
// Computed properties to check book state
var isNotReadyToRead: Bool
var isBeingRead: Bool
// Functions to:
// - Get the first page from the database
// - Show the cover page
// - Restore reader state from the database
// - Begin reading from the first page
// - Advance to a specific page
// - Get details of the current page
// - Save reader state to the database
// - Get edges (choices) for the current page
}View Models
PageViewModel
Manages data related to displaying a single page.
@Observable
class PageViewModel: Observable {
var page: Page? // Details of the current page being read
// Initializer loads the current page details from the database
}EdgesViewModel
Manages data related to displaying the choices for the current page.
@Observable
class EdgesViewModel: Observable {
var edges: [Edge]? // List of possible choices from the current page
// Initializer loads the edges for the current page from the database
}Views
AppEntryView
The entry point of the app that manages authentication.
struct AppEntryView: View {
@State var isAuthenticated = false // Tracks authentication state
// Shows either BookView (if authenticated) or AuthView (if not)
// Monitors Supabase authentication state changes
}AuthView
Handles anonymous authentication with Supabase.
struct AuthView: View {
@State var result: Result<Void, Error>? // Tracks authentication result
// Tries to restore an existing session or create a new anonymous session
// Shows progress while authenticating
}BookView
The main view that orchestrates the reading experience.
struct BookView: View {
@State private var book = BookStore() // Tracks overall reading state
@State private var showingStatsView = false // Controls statistics view
@State private var showingSettingsView = false // Controls settings view
// Shows either CoverView or PageView based on reading state
// Provides toolbar buttons for statistics and settings
// Responds to app lifecycle events to save/restore state
// Applies dark/light mode based on reader preference
}CoverView
Displays the book cover and start button.
struct CoverView: View {
@Environment(BookStore.self) var book // Access to book state
// Shows loading indicator or book cover with "Begin reading" button
// Begins reading when button is tapped
}PageView
Displays the content of a single page in the story.
struct PageView: View {
@Environment(BookStore.self) var book // Access to book state
let viewModel: PageViewModel // View model for the page
// Shows loading indicator while page loads
// Displays page narrative text with Markdown formatting
// Shows page image if available
// Shows "The End" for ending pages
// Otherwise displays EdgeView for available choices
}EdgesView
Displays the choices available from the current page.
struct EdgesView: View {
@Environment(BookStore.self) var book // Access to book state
let viewModel: EdgesViewModel // View model for edges
@State private var showingQuizView = false // Controls quiz display
@State private var quizResult: QuizResult = .quizNotActive // Tracks quiz result
// Shows loading indicator while edges load
// Displays prompts for available choices
// Navigates to new page when choice is tapped
// Shows vocabulary quiz when appropriate
}VocabularyQuizView
Displays vocabulary quizzes that can occur during reading.
struct VocabularyQuizView: View {
@Binding var showing: Bool // Controls whether view is showing
@Binding var result: QuizResult // Tracks quiz result
// In the starter code, this simulates a quiz with buttons for correct/incorrect answers
}StatsView
Displays reading statistics.
struct StatsView: View {
@Binding var showing: Bool // Controls whether view is showing
// Shows statistics about reading progress
// Includes "Done" button to dismiss the view
}SettingsView
Allows readers to adjust app settings.
struct SettingsView: View {
@Binding var showing: Bool // Controls whether view is showing
@Environment(BookStore.self) var book // Access to book state
// Provides toggle for dark mode preference
// Includes "Done" button to dismiss the view
}Database Integration
The app uses Supabase for data storage and authentication:
let supabase = SupabaseClient(
supabaseURL: URL(string: "REDACTED")!,
supabaseKey: "REDACTED"
)The database contains the following tables:
page: Stores page content (narrative text, images, ending information)edge: Stores connections between pages (choices)ending_type: Stores types of story endingsreader: Stores reader preferences and state
Flow of Operation
-
App Launch:
AppEntryViewchecks if user is authenticated- If not,
AuthViewcreates anonymous authentication
-
Reading Begins:
BookViewshowsCoverViewinitially- User taps “Begin reading” to start
BookStore.beginReading()sets current page to first pageBookViewtransitions to showPageView
-
Reading a Page:
PageViewModelloads current page details from databasePageViewdisplays narrative text and optional imageEdgesViewModelloads available choices from databaseEdgesViewdisplays these choices as tappable prompts
-
Making Choices:
- User taps a choice in
EdgesView - If choice triggers a quiz,
VocabularyQuizViewappears - Otherwise,
BookStore.read()updates current page ID - Views refresh to show new page content
- User taps a choice in
-
Story Endings:
- If current page has an
endingTypeId, it’s an ending PageViewshows “The End” instead of choices- User can tap to return to cover
- If current page has an
-
Settings & Stats:
- User can access
SettingsViewto toggle dark mode - User can view
StatsViewto see reading statistics - Changes to settings are saved to database
- User can access
-
State Persistence:
- When app becomes inactive, state is saved to database
- When app becomes active, state is restored from database
- Allows reader to continue where they left off
Quiz Feature
The app has a vocabulary quiz feature:
- Triggered by specific edges (those with “Turn to the next page” prompt)
VocabularyQuizViewdisplays questions (simulated in starter code)- Reader can only proceed after answering correctly
Custom Features
The starter code includes several features that enhance the digital experience beyond a traditional printed book:
- Dark mode toggle
- Progress tracking
- Multimedia content (images)
- Quiz integration
- Statistics tracking
Markdown Support
The app supports Markdown in narrative text and prompts:
Text(
try! AttributedString(
markdown: page.narrative,
options: AttributedString.MarkdownParsingOptions(
interpretedSyntax: .inlineOnlyPreservingWhitespace
)
)
)This allows for rich text formatting within the story content.