Group Group Group Group Group Group Group Group Group

SwiftUI: Using onAppear doesn't allow Picker value to be changed

Hello,

I’m building a Edit View, that allows a user to make updates to a list item and save it back to core data and the list. To do this I am passing in the values from the list in the appropriate fields using the .onAppear action.

All data is passed in appropriately, and the toggle and text fields allow me to make changes easily and save them back to core data and update the list item. However, if I try to change the picker value (emojiChoice) and select a new one, the picker value does not change or save back to Core Data.

When I comment out the onAppear action for the picker variable (emojiChoice), the picker now allows me to choose the value I want and save it back to Core Data. I also have another view that allows a user to create items that is near identical to this Edit View, minus the onAppear action, that works as well. The picker allows a user to choose and emoji. Because there are alot of emoji’s, I created an array and instead am just passing the array index of a selected emoji as an Int.

How can I fix this to allow the picker to show the value to be edited and also allow it to be changed?

Here’s the code of the Edit View:

import CoreData
import SwiftUI

struct editRemindr: View {
@Environment(\.managedObjectContext) var moc
@Environment(\.presentationMode) var presentationMode

let emojiList = EmojiList()
@ObservedObject var reminder: ReminderEntity
@State private var showingDeleteAlert = false

// Form Variables
@State var notifyOn = true
@State var emojiChoice = 0
@State var notification: String
@State var notes: String

// Delete Reminder Function
func deleteAction() {
    moc.delete(reminder)
    // try? self.moc.save()
    presentationMode.wrappedValue.dismiss()
}

// View Controller
var body: some View {
    Form {

        // On/Off Toggle
        Toggle(isOn: $notifyOn) {
            Text("On/Off")
        }

        // Emoji Picker
        Picker(selection: $emojiChoice, label: Text("Emoji")) {
            ForEach(0 ..< emojiList.emojis.count) {
                Text(self.emojiList.emojis[$0])
            }
        }

        // Notification Text
        Section(header: Text("NOTIFICATION")) {
            HStack {
                TextField("Write your notification...", text: $notification)
                    .onReceive(notification.publisher.collect()) {
                        self.notification = String($0.prefix(60)) // <---- SET CHARACTER LIMIT
                }
                Text("\(notification.count) / 60")
                    .font(.caption)
                    .foregroundColor(.gray)
            }
        }

        // Notes Text
        Section(header: Text("NOTES")) {
            VStack {
                MultiLineTextField(text: $notes).frame(numLines: 6)
                    .padding(.top, 5)
                    .onReceive(notes.publisher.collect()) {
                        self.notes = String($0.prefix(240)) // <---- SET CHARACTER LIMIT
                }
                Text("\(notes.count) / 240")
                    .font(.caption)
                    .foregroundColor(.gray)
                    .frame(maxWidth: .infinity, alignment: .trailing)
            }
        }

        // Save Changes Button
        Section() {
            Button (action: {
                self.reminder.dateCreated = Date()
                self.reminder.notifyOn = self.notifyOn
                self.reminder.emojiChoice = Int64(self.emojiChoice)
                self.reminder.notification = self.notification
                self.reminder.notes = self.notes

                try? self.moc.save()

                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("SAVE CHANGES")
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .font(.body)
            }        .padding()
                .frame(maxWidth: .infinity)
                .background(Color.green)
                .padding(.vertical, -6)
                .padding(.horizontal, -15)

            // Cancel Button
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Cancel")
                    .frame(maxWidth: .infinity)
            }
        }
    }

    // Make List Items Appear in Fields
    .onAppear(perform: {
        self.notifyOn = self.reminder.notifyOn
        self.emojiChoice = Int(self.reminder.emojiChoice)
        self.notification = self.reminder.notification ?? "unknown"
        self.notes = self.reminder.notes ?? "unknown"
    }) 
        .alert(isPresented: $showingDeleteAlert) {
            Alert(title: Text("Delete Reminder"), message: Text("Are you sure you want to delete this Reminder?"), primaryButton: .destructive(Text("Delete")) {
                self.deleteAction()
                }, secondaryButton: .cancel()
            )
    }
    .navigationBarTitle("Edit Reminder")
    .navigationBarItems(trailing: Button(action: {self.showingDeleteAlert = true
    }) {
        Image(systemName: "trash")

    })
} 

// Creates Text Limits
class TextLimit: ObservableObject {
    @Published var text = "" {
        didSet {
            if text.count > characterLimit && oldValue.count <= characterLimit {
                text = oldValue
            }
        }
    }
    let characterLimit: Int

    init(limit: Int = 5){
        characterLimit = limit
    }

}

Here’s the code for the Content View with the list if it helps as well.

import SwiftUI
import CoreData

struct ContentView: View {

@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: ReminderEntity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ReminderEntity.dateCreated, ascending: false)])
var reminder: FetchedResults<ReminderEntity>

@State private var showingAddScreen = false
@State var showWelcomeScreen = false

let emojiList = EmojiList()

//Toggle Control
@State var notifyOn = true

// Save Items Function
func saveItems() {
    do {
        try moc.save()
    } catch {
        print(error)
    }
}

// Delete Item Function
func deleteItem(indexSet: IndexSet) {
    let source = indexSet.first!
    let listItem = reminder[source]
    moc.delete(listItem)
}

// View Controller
var body: some View {
    VStack {
        NavigationView {
            ZStack (alignment: .top) {

                // List View
                List {
                    ForEach(reminder, id: \.self) { notification in
                        NavigationLink(destination: editRemindr(reminder: notification, notification: notification.notification ?? "unknown", notes: notification.notes ?? "unknown")) {
                            // Text within List View
                            HStack {
                                // MARK: TODO
                                // Toggle("NotifyOn", isOn: true)
                                // .labelsHidden() // Hides the label/title
                                Text("\(self.emojiList.emojis[Int(notification.emojiChoice)]) \(notification.notification!)")
                            } 
                        }
                    } 
                    .onDelete(perform: deleteItem)
                }

                    // Navigation Items
                    .navigationBarTitle("", displayMode: .inline)
                    .navigationBarItems(
                        leading:
                        HStack {

                            Button(action: {
                                self.showWelcomeScreen.toggle()
                            }) {
                                Image(systemName: "info.circle.fill")
                                    .font(.system(size: 24, weight: .regular))
                            }.foregroundColor(.gray)

                            // Positioning Remindr Logo on Navigation
                            Image("remindrLogoSmall")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                //.frame(width: 60, height: 60, alignment: .center)
                                .padding(.leading, 83)
                                .padding(.top, -10)
                        },

                        // Global Settings Navigation Item
                        trailing: NavigationLink(destination: globalSettings()){
                            Image("settings")
                                .font(Font.title.weight(.ultraLight))
                        }.foregroundColor(.gray)
                )

                // Add New Reminder Button
                VStack {
                    Spacer()
                    Button(action: { self.showingAddScreen.toggle()
                    }) {
                        Image("addButton")
                            .renderingMode(.original)
                    }
                    .sheet(isPresented: $showingAddScreen) {

                        newRemindr().environment(\.managedObjectContext, self.moc)

                    } 
                }
            }
        }  .sheet(isPresented: $showWelcomeScreen) {
            welcomeScreen()
        }
    }
}

Any help would be greatly appreciated! Thank you so much!