12‐Zhaoquan_Zheng - molab-itp/content-2026-itp GitHub Wiki
Swift Learning Progress
Week 1
For Week 1, I started learning Swift fundamentals through the first part of 100 Days of SwiftUI.
I focused on:
- Variables and constants
- Strings and numbers
- Booleans
- String interpolation
- Arrays, dictionaries, sets, and enums
- Type annotations
ifstatementsswitch- The ternary operator
forloops- Functions
- Parameters and return values
This week helped me become familiar with basic Swift syntax and how values are stored and changed.
I especially practiced using arrays, loops, random numbers, and functions in my Week 1 Playground assignment.
One concept that became clearer to me was how values can accumulate inside a loop. For example:
line += symbols[randomInt]
Week 2
For Week 2, I continued learning Swift fundamentals through the next section of 100 Days of SwiftUI.
I focused on:
- Default parameter values
- Throwing functions
- Error handling with
do,try, andcatch - Closures
- Passing functions into functions
- Structs
- Stored and computed properties
- Property observers
- Access control
- Static properties and methods
- Classes
- Inheritance
- Protocols
- Extensions
- Optionals
- Nil coalescing
This week was more challenging because many of the concepts were more abstract than the topics from Week 1.
I spent more time understanding closures, computed properties, and optionals. I also started to understand how Swift handles errors and why functions can use throw instead of returning a normal value.
Week 3
This week I started learning SwiftUI and began applying the Swift fundamentals from the previous two weeks to a real app interface.
I focused on:
- SwiftUI
View bodyVStackNavigationStackNavigationLink- Creating multiple views
@State- Arrays
- Random numbers
ForEachLazyVGrid
The biggest change for me was moving from Playgrounds to SwiftUI.
In a Playground, I usually used print() to see the result of my code. In SwiftUI, I started learning how to describe what should appear on the screen using different Views.
For my Week 3 homework, I created a multi-view SwiftUI app called Random Bunny App.
The first screen contains a button that navigates to a second screen called PatternView.
In PatternView, I created an array of visual elements:
let symbols = ["🐰", "🌸", "✨", "🥕"]
I then used a random number to choose an element from the array:
let randomIndex = Int.random(in: 0..<symbols.count)
A loop generates 16 random elements, and LazyVGrid displays them in a 4 × 4 pattern.
For example:
🐰 🌸 🐰 ✨ ✨ 🥕 🌸 🐰 🥕 🐰 ✨ 🌸 🌸 🥕 🐰 ✨
I also added a Generate Again button so the user can create a new random pattern.
This project helped me connect the Swift fundamentals I practiced in the first two weeks, especially arrays, loops, functions, and random numbers, with SwiftUI.
Week 04
This week I continued working with SwiftUI and learned more about state, timers, navigation, and audio playback.
Some of the main things I practiced were:
@StateNavigationViewNavigationLink- Creating multiple views
Timer.publish().onReceive()@Environment@ObservableAVAudioPlayer- Playing and stopping audio
The biggest new thing for me this week was learning how SwiftUI can respond to time and audio.
For my Week 4 homework, I created a two-page SwiftUI app called Focus Bunny.
The first page is a simple focus timer. It starts at 30 seconds and has three buttons: Start, Pause, and Reset.
I used @State to store the remaining time and whether the timer is playing:
@State var secondsRemaining = 30
@State var isPlaying = false
I used a timer that updates every second:
let timer = Timer.publish(
every: 1,
on: .main,
in: .common
).autoconnect()
Then I used .onReceive() to decrease the remaining time while the timer is running:
.onReceive(timer) { _ in
if isPlaying {
if secondsRemaining > 0 {
secondsRemaining -= 1
}
}
}
The Start, Pause, and Reset buttons change the state of the timer:
Button("Start") {
isPlaying = true
}
Button("Pause") {
isPlaying = false
}
Button("Reset") {
secondsRemaining = 30
isPlaying = false
}
The app also has a second page called Focus Sounds.
I used NavigationLink to move from the timer page to the sound page:
NavigationLink(destination: SoundView()) {
Text("Focus Sounds")
}
On the second page, the user can play or stop a rain sound.
I used AVAudioPlayer to load and play a local .mp3 file:
func play() {
player = loadBundleAudio("rain.mp3")
player?.numberOfLoops = -1
player?.play()
}
func stop() {
player?.stop()
}
I also practiced sharing data between views using @Environment:
@Environment(AudioDJ.self) var audioDJ
The Play and Stop buttons call the audio functions:
Button("Play") {
audioDJ.play()
}
Button("Stop") {
audioDJ.stop()
}
This project helped me understand how SwiftUI state can control things that change over time instead of only displaying static content.
It also helped me understand how multiple views can share the same data and functions through the environment.
Compared with my Week 3 project, this app feels more interactive because it responds to both time and audio.
Week 05
This week I continued developing my Focus Bunny navigation app from Week 04.
For this week's homework, I incorporated ideas from the class demos into my existing app instead of creating a completely new project.
The main class demos I used were:
05-AppStorageDemo05-TimerDemo
AppStorage
The biggest new thing I learned this week was @AppStorage.
I used @AppStorage to save the number of focus sessions:
@AppStorage("focusSessions") var focusSessions: Int = 0
I display the saved number on the main page:
Text("Focus Sessions: \(focusSessions)")
.font(.title2)
I also added two buttons so the user can increase or decrease the number of sessions:
HStack {
Button("+ Session") {
focusSessions += 1
}
.frame(width: 100, height: 40)
.background(Color.gray)
.foregroundColor(Color.white)
.cornerRadius(20)
Button("- Session") {
focusSessions -= 1
}
.frame(width: 100, height: 40)
.background(Color.gray)
.foregroundColor(Color.white)
.cornerRadius(20)
}
The important difference I noticed between @State and @AppStorage is that the value stored with @AppStorage remains after I stop and reopen the app.
For example, if the user changes:
Focus Sessions: 0
to:
Focus Sessions: 3
the app still shows 3 after it is closed and opened again.
Timer Interaction
I also incorporated an interaction from the 05-TimerDemo.
In the class example, tapping the timer increases the time. I added the same idea to my Focus Bunny timer.
Text("\(secondsRemaining)")
.font(.system(size: 80))
.onTapGesture {
secondsRemaining += 10
}
Text("Tap on time to increase")
.foregroundColor(.gray)
Now the user can tap the displayed time to add 10 seconds.
For example:
30 → 40 → 50 → 60
Timer Buttons
I also adjusted the visual style of the existing Start, Pause, and Reset buttons using properties shown in the Timer demo.
HStack {
Button("Start") {
isPlaying = true
}
.frame(width: 80, height: 40)
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(20)
Button("Pause") {
isPlaying = false
}
.frame(width: 80, height: 40)
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(20)
Button("Reset") {
secondsRemaining = 30
isPlaying = false
}
.frame(width: 80, height: 40)
.background(Color.black)
.foregroundColor(Color.white)
.cornerRadius(20)
}
This helped make the timer controls more visually consistent while keeping the code simple.
Current App Structure
The app now includes:
- A focus timer
- Start, Pause, and Reset controls
- Tap the timer to add 10 seconds
- Focus session tracking with
@AppStorage - Saved session data after reopening the app
- Navigation to a second page
- Rain sound playback
- Play and Stop audio controls
The basic navigation structure from Week 04 is still the same:
Focus Bunny
↓
Focus Timer
↓
Focus Sessions
↓
Focus Sounds
What I Learned
This week I practiced:
@AppStorage- Saving simple user data
- The difference between
@Stateand@AppStorage .onTapGesture- Updating a timer through user interaction
- Reusing ideas from class demos in an existing SwiftUI project
- Continuing to improve a navigation app instead of starting over
- Using
.frame() - Using
.background() - Using
.foregroundColor() - Using
.cornerRadius()
The most useful thing I learned was that @AppStorage can save small pieces of user data very easily.
Before this week, most of the values in my app reset when the app restarted. Now the Focus Sessions value can persist between app launches.
I also learned that I can take a small interaction from a class demo, such as tapping the timer to add time, and adapt it to my own project without changing the overall app structure.
Week 06
This week I continued developing my Focus Bunny app from previous weeks.
For Week 06, I focused on saving and restoring data using JSON. I also continued practicing Swift fundamentals and reused techniques from the class demos.
Progress
I added a new feature called Bunny Focus Log.
Each time the user adds a focus session to the log, the app creates a FocusRecord:
struct FocusRecord: Identifiable, Codable {
var id = UUID()
var seconds: Int
}
For example:
🥕 30 seconds
🥕 40 seconds
🥕 50 seconds
I used an array to store the focus records:
@State var focusRecords: [FocusRecord] = []
The user can add the current timer value to the log:
Button("Add to Focus Log") {
focusRecords.append(
FocusRecord(seconds: secondsRemaining)
)
}
I used ForEach to display the saved focus records on the screen:
ForEach(focusRecords) { record in
HStack {
Text("🥕")
Text("\(record.seconds) seconds")
}
}
JSON Saving and Loading
I used the JSON saving and loading approach from the Week 06 class demos.
The app can now:
- Save the Bunny Focus Log as JSON
- Clear the current log
- Restore the saved log from the JSON file
The saved file is:
focusRecords.json
I used:
JSONEncoder()
to convert the Swift data into JSON.
I used:
JSONDecoder()
to convert the JSON file back into Swift data.
The app has three data controls:
Save
Clear
Restore
This made it easy to test whether the saved data was actually being restored.
For example:
1. Add several focus records
2. Save the log
3. Clear the log
4. Restore the log
5. The previous records appear again
Current App
The app now includes:
- Focus timer
- Start, Pause, and Reset controls
- Tap the timer to add 10 seconds
- Focus Sessions using
@AppStorage - Bunny Focus Log
- JSON Save and Restore
- Rain sound playback
- Navigation between the timer and sound page
Problems
One issue I encountered was a warning in Xcode when reading the JSON file:
'init(contentsOfFile:)' was deprecated in iOS 18
The code still works, but Xcode recommends a newer version of this API.
For this assignment, I kept the same approach used in the class JSON demo so I could better understand the class example before changing it.
Another challenge was deciding what data would be meaningful to save.
Instead of only saving one number, I decided to save individual focus records so the saved data became part of the actual app experience.
Plans
Next, I would like to continue improving Focus Bunny without making the project too complicated.
Possible next steps are:
- Improve the Focus Log
- Explore saving more information about each focus session
- Continue improving navigation
- Continue practicing Swift fundamentals
- Explore more SwiftUI examples from class
What I Learned
This week I learned more about persistent data.
In Week 05, I used @AppStorage to save a simple value.
In Week 06, I learned how to save a collection of custom data using:
Codable
JSONEncoder
JSONDecoder
FileManager
I also learned how Identifiable and ForEach can be used together to display a collection of saved records.
The Bunny Focus Log helped me understand how saved data can become part of an app feature instead of only being used as a technical exercise.