In this exercise you will examine a small SwiftUI app built using the MVVM design pattern:

Working with a partner:
- Read through the source code carefully.
- Download a copy of the project and try it out together.
- Then work together to respond to each question below on the paper provided.
- Finally, check your responses by expanding the example solutions provided.
Source Code
import Foundation
import Observation
import SwiftUI
// MODEL
struct ChemistryTerm: Identifiable {
// MARK: Stored properties
let id = UUID()
let term: String
let definition: String
var isRevealed: Bool = false
}
// VIEW MODEL
@Observable
class ChemistryTermViewModel {
// MARK: Stored properties
var terms: [ChemistryTerm] = [
ChemistryTerm(
term: "Atom",
definition: "The smallest unit of an element that retains its chemical properties."
),
ChemistryTerm(
term: "Isotope",
definition: "Atoms of the same element that have different numbers of neutrons."
),
ChemistryTerm(
term: "Mole",
definition: "An SI unit representing 6.022 × 10²³ particles of a substance."
),
ChemistryTerm(
term: "Electronegativity",
definition: "The tendency of an atom to attract shared electrons toward itself in a bond."
),
ChemistryTerm(
term: "Oxidation",
definition: "A process in which an atom or ion loses one or more electrons."
),
ChemistryTerm(
term: "Catalyst",
definition: "A substance that increases the rate of a reaction without being consumed."
),
ChemistryTerm(
term: "Enthalpy",
definition: "The total heat content of a system measured at constant pressure."
),
ChemistryTerm(
term: "Molarity",
definition: "A measure of concentration equal to moles of solute per litre of solution."
),
ChemistryTerm(
term: "Valence Electrons",
definition: "Electrons occupying the outermost energy level of an atom."
),
ChemistryTerm(
term: "Le Chatelier's Principle",
definition: "A system at equilibrium will shift to partially offset any stress applied to it."
),
]
// MARK: Functions
func reveal(termToReveal: ChemistryTerm) {
for index in terms.indices {
if terms[index].id == termToReveal.id {
if terms[index].isRevealed == false {
terms[index].isRevealed = true
} else {
terms[index].isRevealed = false
}
}
}
}
}
// VIEW
struct ContentView: View {
// MARK: Stored properties
@State var viewModel = ChemistryTermViewModel()
// MARK: Computed properties
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 12) {
Text("Tap to toggle between each definition or term.")
.foregroundStyle(.secondary)
ForEach(viewModel.terms) { term in
Button {
viewModel.reveal(termToReveal: term)
} label: {
VStack(alignment: .leading, spacing: 6) {
if term.isRevealed {
Text(term.term)
.font(.headline)
.foregroundStyle(.white)
} else {
Text(term.definition)
.font(.subheadline)
.foregroundStyle(.white)
.multilineTextAlignment(.leading)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(term.isRevealed ? Color.green.mix(with: .black, by: 0.2) : Color.blue.mix(with: .black, by: 0.2))
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
}
.padding()
}
.navigationTitle("Chemistry Review")
}
}
}
#Preview {
ContentView()
}Review Questions
Question 1
Describe the overall purpose of this program.
SOLUTION
The purpose of this program is to help users review Chemistry vocabulary. The app displays a list of definitions as buttons. When the user taps a button, the corresponding Chemistry term is revealed in its place, and tapping again returns the button to showing the definition. This allows a student to quiz themselves by reading a definition, recalling the term, and then checking their answer — and to hide the answer again if they want to re-test themselves.
Question 2
Describe where input and output occur in the running of the program. Identify the specific line(s) of code responsible for each.
SOLUTION
Input occurs when the user taps one of the buttons. On line 93, the
Buttonaction block captures this tap, and on line 94,viewModel.reveal(termToReveal: term)is called to pass the selected term to the view model.Output occurs when the view displays either a definition or a revealed term for each button. On line 97, the
if term.isRevealedcondition determines what is shown: whenfalse, line 102 displaysText(term.definition); whentrue, line 98 displaysText(term.term). Additionally, line 110 changes the button’s background colour between dark blue and dark green to visually indicate the current state of each card.
Question 3
Identify where data is first stored in an array. Reference the specific line(s) of code that accomplish this.
SOLUTION
Data is first stored in an array on line 21, where the
termsproperty is declared and immediately initialized. TenChemistryTerminstances are created inline, beginning on line 22 with the entry for “Atom” and ending on line 61 with the closing of the last entry. Each instance is constructed using theChemistryTermstruct’s initializer, providing atermstring, adefinitionstring, and relying on the default value offalseforisRevealedas defined on line 12. This initialization happens as soon as aChemistryTermViewModelobject is created.
Question 4
What is the name of the chosen array?
SOLUTION
The array is named
terms. It is declared as a stored property of theChemistryTermViewModelclass on line 21.
Question 5
Describe where the chosen array is used in the program — for example, where it is iterated over, where individual elements are accessed, or where existing data in the array is used to produce new data or drive behaviour.
SOLUTION
The array is used in two places. First, on line 92,
ForEach(viewModel.terms)iterates over every element in the array to produce one button per term. For each element, thedefinitionandisRevealedproperties are read on lines 97–105 to determine what text and background colour to display.Second, inside the
reveal(termToReveal:)function, line 66 iterates over the array usingfor index in terms.indices. On line 67, each element’sidis compared to the tapped term’sid. When a match is found, lines 68–72 read the current value ofisRevealedand toggle it to the opposite state. This represents the array being used to both read existing data and update a specific element’s state.
Question 6
What does the data stored in the array represent within the program? Describe the type of data stored and what role it plays.
SOLUTION
Each element of the
termsarray is an instance of theChemistryTermstruct, defined on lines 6–13. Each instance represents one Chemistry vocabulary item, holding three pieces of data: the term itself (aString, line 10), its definition (aString, line 11), and whether the term is currently revealed (aBool, line 12). Together, the elements of the array represent the complete set of flashcard-style content that the app presents to the user.
Question 7
Explain how the terms array manages complexity in this program. In your response, explain why the program could not be written — or how it would have to be written differently — if the array were not used.
SOLUTION
The
termsarray manages complexity by allowing all ten Chemistry vocabulary items to be stored, accessed, and updated through a single named collection rather than as ten separate variables. Without the array, each term would need its own individual variables (e.g.,term1,definition1,isRevealed1,term2,definition2,isRevealed2, and so on), resulting in thirty separate variables to represent the same data.The view code would also be far more complex without the array. The
ForEachon line 92 depends on being able to iterate over a collection; without the array, each of the ten buttons would need to be written out individually, and any change to the layout or logic would need to be applied ten separate times. Similarly, theforloop on line 66 relies on being able to iterate throughterms.indicesto locate and toggle the correct element — without the array, a separateif-statement would be required for each of the ten terms. The array therefore makes the program significantly shorter, easier to read, and easier to maintain.
Question 8
Identify where a function is defined in the program. What is the function’s name? What is its return type, if any? What parameter(s) does it define, and how do they affect what happens inside the function?
SOLUTION
The function is defined on line 65. Its name is
reveal. It has no return type — it performs an action rather than computing and returning a value, so its return type is implicitlyVoid.The function defines one parameter:
termToRevealof typeChemistryTerm. This parameter receives the specific term that the user tapped. Inside the function, theidoftermToRevealis used on line 67 as the value to compare against when searching through the array, so the parameter directly determines which element will have itsisRevealedproperty toggled.
Question 9
Where is this function called in the program?
SOLUTION
The function is called on line 94, inside the
Buttonaction block inContentView. The call is written asviewModel.reveal(termToReveal: term), wheretermis the specificChemistryTerminstance that theForEachloop on line 92 is currently iterating over. This means the function is called once each time the user taps any of the ten buttons.
Question 10
What is the purpose of the reveal function, and how does it contribute to the overall functionality of the program?
SOLUTION
The purpose of the
revealfunction is to locate a specificChemistryTermin thetermsarray by matching unique identifiers, then toggle that term’sisRevealedproperty betweentrueandfalse.It contributes to the program’s overall functionality by connecting user input to a data change. When the user taps a button, the view calls
reveal, the view model updates the array, and becausetermsis a tracked property under the@Observablemacro, SwiftUI automatically re-renders the affected button — switching it between showing the definition on a blue background and showing the term on a green background.
Question 11
Explain in detailed steps how the reveal function uses sequence, selection, and/or iteration to accomplish its purpose. Your explanation should be detailed enough for someone else to reproduce the function.
SOLUTION
- (Sequence) The function begins by receiving
termToReveal, aChemistryTermvalue, as its input parameter.- (Iteration) On line 66, a
forloop begins, iterating over every valid index in thetermsarray usingterms.indices. The loop visits each index from the first element to the last, one at a time.- (Selection) On line 67, an outer
ifstatement evaluates whether theidof the element at the current index —terms[index].id— equals theidoftermToReveal. Because eachChemistryTermis assigned a uniqueUUIDon creation, this condition will betruefor exactly one element in the array.- (Selection) If the outer condition is
true, an innerifstatement on line 68 checks whetherterms[index].isRevealedis currentlyfalse.- (Sequence) If the inner condition is
true(the term is not yet revealed), line 69 setsterms[index].isRevealed = true.- (Sequence) If the inner condition is
false(the term is already revealed), theelsebranch on line 71 setsterms[index].isRevealed = false.- (Iteration continues) The loop does not exit early after finding a match — it continues through the remaining indices, though no further matches will be found.
- (Sequence) Once all indices have been visited, the function ends and returns no value.
Question 12
Describe a scenario where two separate calls to the reveal function, at different points while the program is running, would result in different outcomes.
SOLUTION
Consider a user who first taps the “Atom” button, then taps the same “Atom” button a second time.
In the first call,
revealis called with theChemistryTermfor “Atom”, whoseisRevealedis currentlyfalse. The function finds the matching element, the innerifcondition on line 68 istrue, and line 69 setsisRevealedtotrue. The button updates to show the word “Atom” on a green background.In the second call,
revealis called again with the same “Atom” term. The function finds the same matching element, but this timeisRevealedistrue. The innerifcondition on line 68 is nowfalse, so theelsebranch on line 71 executes instead, settingisRevealedback tofalse. The button returns to showing the definition on a blue background.
Question 13
Describe the condition tested by each of the two calls identified above.
SOLUTION
In both calls, the outer condition on line 67 —
terms[index].id == termToReveal.id— is the same: it checks whether the current array element’s unique identifier matches the identifier of the term that was tapped. This condition evaluates totruefor the “Atom” element in both calls.The difference lies in the inner condition on line 68:
terms[index].isRevealed == false. During the first call,isRevealedisfalse, so this condition istrueand theifbranch runs. During the second call,isRevealedistrue, so this condition isfalseand theelsebranch runs instead.
Question 14
What will the result of the first call be? What will the result of the second call be?
SOLUTION
After the first call, the
ChemistryTermfor “Atom” has itsisRevealedproperty changed fromfalsetotrue. SwiftUI detects the change to thetermsarray and re-renders that button: the definition text is replaced by the term “Atom” in a bold headline font, and the background colour changes from dark blue to dark green.After the second call,
isRevealedis changed back fromtruetofalse. SwiftUI re-renders the button again: the term is replaced by the definition in a smaller subheadline font, and the background colour returns to dark blue. The button is now in the same visual state it was in before the first tap.