SwiftUI Save New Object and Navigate to it

Viewed 318

When the NavigationLink is pressed I want to create an object (with the time when it was pressed), add it to the savedObjects and pass then new object to the destination view. How can I do this without changing the state while the view is updating?

struct ContentView: View {

    @State private var savedObjects = [
        MyObject(
            id: 0,
            date: Date()
        ),
        MyObject(
            id: 1,
            date: Date()
        ),
        MyObject(
            id: 2,
            date: Date()
        ),
        MyObject(
            id: 3,
            date: Date()
        )
    ]

    var body: some View {
        NavigationView {
            List {
                NavigationLink("Save new object and navigate to it", destination: DestinationView(object: MyObject(id: Int.random(in: 10...1000), date: Date())))
                ForEach(savedObjects) { object in
                    NavigationLink("Navigate to object \(object.id)", destination: DestinationView(object: object))
                }
            }
        }
    }
}

class MyObject: ObservableObject, Identifiable {
    var id: Int
    var date: Date

    init(id: Int, date: Date) {
        self.id = id
        self.date = date
    }
}

struct DestinationView: View {

    @ObservedObject var object: MyObject

    var body: some View {
        VStack {
            Text("object \(object.id)")
            Text("date: \(object.date.description)")
        }
    }
}
3 Answers

Here is a solution that refreshes state with a new Date when the link appears.

struct LinkWithPayloadView: View {
    @State private var date = Date()
    
    var body: some View {
        NavigationView {
            navigationLink(payload: "Cookies and Milk", date: date)
                .onAppear(perform: {
                    date = Date()
                })
        }
    }
    
    func navigationLink(payload: String, date: Date) -> some View {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss"
        let payload = Payload(timestamp: date, inners: payload)

        let vstack = VStack {
            Text("\(payload.inners)")
            Text("\(formatter.string(from: payload.timestamp))")
        }

        return NavigationLink(payload.inners, destination: vstack)
    }
    
    struct Payload {
        let timestamp : Date
        let inners : String
    }
}

Credit @mallow for the idea

My question is why can't you make a separate destination view to save the object? Then you don't need to worry about saving it in the starting view.

struct DestinationView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@ObservedObject var object: MyObject

init(id : Int, date : Date) {
    let myObject = MyObject(context: managedObjectContext)
    myObject.date = date
    myObject.id = id
    try! managedObjectContext.save()
    self.object = myObject
}

var body: some View {
    VStack {
        Text("object \(object.id)")
        Text("date: \(object.date.description)")
    }
}
}

Then you can just reload the objects from coredata when the navigation closes.

Related