Last active
December 6, 2024 12:48
-
-
Save JohnSundell/889c916a350be4b18951c725c84f3011 to your computer and use it in GitHub Desktop.
Timer-based example used during my Advanced SwiftUI workshop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | |
#Preview { | |
TimerList() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment