Skip to content

Instantly share code, notes, and snippets.

@michaelevensen
Created December 21, 2022 12:36
Show Gist options
  • Save michaelevensen/30b451acc08aad4306fbc7ab5b03139b to your computer and use it in GitHub Desktop.
Save michaelevensen/30b451acc08aad4306fbc7ab5b03139b to your computer and use it in GitHub Desktop.
Example of how you can create a Master Detail data editing and creation flow in SwiftUI.
//
// BindingTest.swift
// MutatingState
//
// Created by Michael Nino Evensen on 21/12/2022.
//
import SwiftUI
struct Object: Identifiable {
var id = UUID()
var value: Int
var string: String?
var date: Date?
init(value: Int = 1, string: String? = nil, date: Date? = nil) {
self.value = value
self.string = string
self.date = date
}
struct MutableData {
var value: Int = 1
var string: String = ""
var date: Date = .now
}
var mutableData: MutableData {
return MutableData(value: value, string: string ?? "", date: date ?? .now)
}
mutating func update(with data: MutableData) -> Self {
self.value = data.value
self.string = data.string
self.date = data.date
return self
}
}
struct BindingTest: View {
@State private var showNewItemSheet: Bool = false
@State private var items = [Object]()
// New
@State var new = Object()
var body: some View {
NavigationStack {
List($items) { item in
NavigationLink {
BindingTestDetailView(initialValue: item)
} label: {
HStack {
VStack(alignment: .leading) {
if let title = item.wrappedValue.string {
Text(title)
}
if let date = item.wrappedValue.date {
Text(date.formatted(.dateTime.day().month()))
.foregroundColor(.secondary)
.font(.caption2)
}
}
Spacer()
Text(String(describing: item.wrappedValue.value.formatted(.number)))
.foregroundColor(.secondary)
}
}
}
}
.safeAreaInset(edge: .bottom) {
Button("+ Add new") {
showNewItemSheet = true
}
.buttonStyle(.borderedProminent)
}
.sheet(isPresented: $showNewItemSheet) {
BindingTestDetailView(initialValue: $new, didSave: { newItem in
self.items.append(newItem)
// Reset
new = Object()
})
}
}
}
struct BindingTestDetailView: View {
@Environment(\.dismiss) private var dismiss
@Binding var obj: Object
@State private var didSpecifyDate: Bool = false
// Mutable local state
@State private var mutable = Object.MutableData()
let didSave: ((Object) -> Void)?
init(
initialValue: Binding<Object>,
didSave: ((Object) -> Void)? = { _ in }
) {
self._obj = initialValue
self.didSave = didSave
self._mutable = State(initialValue: initialValue.wrappedValue.mutableData)
if let _ = initialValue.wrappedValue.date {
self._didSpecifyDate = State(initialValue: true)
}
}
var body: some View {
VStack {
Text(String(describing: self.mutable))
TextField("String", text: $mutable.string)
Toggle(isOn: $didSpecifyDate) {
Label("Has Date", systemImage: "calendar")
}
if didSpecifyDate {
DatePicker("Date", selection: $mutable.date, displayedComponents: .date)
}
Stepper(mutable.value.formatted(.number), value: $mutable.value, in: 0...10)
Button("Save") { save() }
.buttonStyle(.borderedProminent)
}
.padding()
}
private func save() {
var updated = self.obj.update(with: mutable)
// Remove date if unspecified
if didSpecifyDate == false {
self.obj.date = nil
updated.date = nil
}
didSave?(updated)
dismiss()
}
}
struct BindingTest_Previews: PreviewProvider {
static var previews: some View {
BindingTest()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment