Skip to content

Instantly share code, notes, and snippets.

@JohnSundell
Last active March 23, 2023 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnSundell/889c916a350be4b18951c725c84f3011 to your computer and use it in GitHub Desktop.
Save JohnSundell/889c916a350be4b18951c725c84f3011 to your computer and use it in GitHub Desktop.
Timer-based example used during my Advanced SwiftUI workshop.
import SwiftUI
// Model + controller:
struct NamedTimer: Identifiable, Equatable {
let id = UUID()
var name: String
var seconds = 0
}
final class TimerController: ObservableObject {
@Published var timers: [NamedTimer]
private lazy var systemTimer = makeSystemTimer()
init() {
timers = [NamedTimer(name: "First timer")]
RunLoop.main.add(systemTimer, forMode: .common)
}
private func makeSystemTimer() -> Timer {
Timer(
timeInterval: 1,
repeats: true,
block: { [weak self] _ in
self?.incrementTimers()
}
)
}
private func incrementTimers() {
timers = timers.map { timer in
var timer = timer
timer.seconds += 1
return timer
}
}
}
// List view:
struct TimerList: View {
@StateObject private var controller = TimerController()
var body: some View {
NavigationView {
List {
ForEach($controller.timers) { $timer in
NavigationLink(destination: {
TimerDetailsView(
viewModel: TimerDetailsViewModel(
timer: timer
)
)
}, label: {
TimerListRow(timer: timer)
})
}
Button("Add timer") {
controller.timers.append(NamedTimer(
name: "New timer"
))
}
}
.navigationTitle("Timers")
}
}
}
struct TimerListRow: View {
var timer: NamedTimer
var body: some View {
HStack {
Text(timer.name)
Spacer()
Text(String(timer.seconds))
.bold()
}
}
}
// Detail view:
final class TimerDetailsViewModel: ObservableObject {
@Published var timer: NamedTimer
@Published var temporaryNotes = ""
init(timer: NamedTimer) {
self.timer = timer
}
func duplicateTimer() {
// To be implemented
}
}
struct TimerDetailsView: View {
@ObservedObject var viewModel: TimerDetailsViewModel
var body: some View {
VStack(spacing: 20) {
TextField("Timer name", text: $viewModel.timer.name)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 200)
Text(String(viewModel.timer.seconds))
.font(.largeTitle)
VStack(alignment: .leading) {
Text("Temporary notes:")
TextEditor(text: $viewModel.temporaryNotes)
.cornerRadius(5)
}
.padding()
.background(Color(.systemGroupedBackground))
}
.padding(.top, 20)
.toolbar {
Button("Duplicate") {
viewModel.duplicateTimer()
}
}
.navigationTitle("Timer details")
.navigationBarTitleDisplayMode(.inline)
}
}
// Preview:
struct TimerList_Previews: PreviewProvider {
static var previews: some View {
TimerList()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment