SwiftUI Picker from CoreData Values
Change your select property to be a User and make it optional (since nothing is selected when the view is opened)
@State var selectedUser: User?
Then you need to use .tag
in your picker so the selected user can be correctly assigned to selectedUser
Picker("Select user", selection: $selectedUser) {
ForEach(users) { user in
Text(user.name!)
.tag(Optional(user))
}
}
.pickerStyle(.menu)
Note that the user
variable needs to be made optional in the tag so its type matches the type of selectedUser
How to use a picker on CoreData relationships in SwiftUI
OK. Huge vote of thanks to Lorem for getting me to the answer. Thanks too for Roma, but it does turn out that his solution, whilst it worked to resolve one of my key problems, does introduce inefficiencies - and didn't resolve the second one.
If others are hitting the same issue I'll leave the Github repo up, but the crux of it all was that @State shouldn't be used when you're sharing CoreData objects around. @ObservedObject is the way to go here.
So the resolution to the problems I encountered were:
- Use @ObservedObject instead of @State for passing around the CoreData objects
- Make sure that the picker has a tag defined. The documentation I head read implied that this gets generated automatically if you use ".self" as the id for the objects in ForEach, but it seems this is not always reliable. so adding ".tag(element as Element?)" to my picker helped here.
Note: It needed to be an optional type because CoreData makes all the attribute types optional.
Those two alone fixed the problems.
The revised "LicenceView" struct is here, but the whole solution is in the repo.
Cheers!
struct LicenceView : View {
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var licence: Licence
@Binding var showModal: Bool
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Element.desc, ascending: true)],
animation: .default)
private var elements: FetchedResults<Element>
func save() {
try! viewContext.save()
showModal = false
}
var body: some View {
VStack {
Button(action: {showModal = false}) {
Text("Close")
}
Picker(selection: $licence.licenced, label: Text("Element")) {
ForEach(elements, id: \.self) { element in
Text("\(element.desc!)")
.tag(element as Element?)
}
}
Text("Selected: \(licence.licenced!.desc!)")
Button(action: {save()}) {
Text("Save")
}
}
}
}
How can I use Core Data value from picker? #SwiftUI #CoreData
I misunderstood your issue with my original comment see below. There is a lot of info in the comments.
import SwiftUI
struct ParentPickerView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \PageName.pageName, ascending: true)],
animation: .default)
private var items0: FetchedResults<PageName>
//State is a source of truth
@State var selectedPage: PageName? = nil
var body: some View {
NavigationView {
Form {
Text(selectedPage?.pageName ?? "not selected")
PickerView(items0: Array(items0), selectedPage: $selectedPage)
}
.foregroundColor(.blue)
.background(Color.yellow)
}
}
}
struct PickerView: View {
//If you don't need all your PageName in your parentView it would be best to have the fetch here
// @Environment(\.managedObjectContext) private var viewContext
// @FetchRequest(
// sortDescriptors: [NSSortDescriptor(keyPath: \PageName.pageName, ascending: true)],
// animation: .default)
// private var items0: FetchedResults<PageName>
var items0: [PageName]
//Binding is a two-way connection
@Binding var selectedPage: PageName?
//You don't need custom init in SwiftUI because they are struct
// init(items0: [PageName], selectedPage: Binding<PageName?>) {
// self.items0 = items0
// self._selectedPage = selectedPage
//
// //@State Init here is bad practice as you are experiencing it becomes a dead end
// //self._selectedPage = State(initialValue: items0.first)
// }
var body: some View {
Picker(selection: $selectedPage, label: Text("Page")) {
ForEach(items0) { item in
Text(item.pageName ?? "").tag(item as PageName?)
}
}
Text("\((selectedPage?.pageName ?? "not selected"))")
.onAppear(){
//Set your initial item here only if the selected item is nil
if selectedPage == nil{
selectedPage = items0.first
}
}
}
}
Choosing CoreData Entities from form picker
As written you are not matching the types of the array the picker and the FetchResult. See the comments
import SwiftUI
@available(iOS 15.0, *)
struct LearnView: View {
//This is optional Language
@State private var selectedLanguage: Language?
//I commented out variables that are not part of the reproducible body provided
//@State private var selectedCategory: SubCategory?
//@State private var selectedDate = Date()
@Environment(\.managedObjectContext) private var viewContext
//This is FetchedResults<Language>
@FetchRequest(entity: Language.entity(), sortDescriptors: []) var languages: FetchedResults<Language>
// @FetchRequest(entity: SubCategory.entity(), sortDescriptors: []) var subCategories: FetchedResults<SubCategory>
var body: some View {
NavigationView {
ZStack {
Form {
Section("Learning Schedule") {
Picker("Please choose a language", selection: $selectedLanguage) {
//Map is needed for a plain array of languages
//ForEach(languages.map{$0}, id: \.self) { language in
//This version works too
//The point is to go from FetchedResults<Language> to [Language]
ForEach(Array(languages), id: \.self) { language in
Text(language.name ?? "Unknown")
//The casting is necessary to make it an optional since your @State is an optional
.tag(language as? Language)
}
}
Text("You selected: \(selectedLanguage?.name ?? "Unknown")")
}
}
}
}
}
}
@available(iOS 15.0, *)
struct LearnView_Previews: PreviewProvider {
static var previews: some View {
LearnView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
Core Data results into a SwiftUI Picker view without preselecting an option
You could set the selectedAction to a non-existent Actions
in init(...)
like this:
self._selectedAction = State(initialValue: Actions(context: context))
that will not set a pre-selected object in the picker.
struct RecordCreateview: View {
@FetchRequest private var actions: FetchedResults<Actions>
@State private var selectedAction: Actions
init(context: NSManagedObjectContext) {
let fetchRequest: NSFetchRequest<Actions> = Actions.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Actions.action, ascending: true)]
fetchRequest.predicate = NSPredicate(value: true)
self._actions = FetchRequest(fetchRequest: fetchRequest)
self._selectedAction = State(initialValue: Actions(context: context)) // <--- here
}
var body: some View {
NavigationView {
Form {
Picker("Select action", selection: $selectedAction){
ForEach(actions) { action in
if action.title == true {
Text("\(action.action!)").tag(action)
}
}
}
}
}
}
}
SwiftUI Core Data Object Return from Picker
A helpful commenter on the Apple forums showed me the answer - which was to do with making sure the selection variable was not optional:
import SwiftUI
import CoreData
struct ContentView: View {
@FetchRequest private var players: FetchedResults<Player>
@State private var selection: Player
init(moc: NSManagedObjectContext) {
let fetchRequest: NSFetchRequest<Player> = Player.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Player.familyName, ascending: true)]
fetchRequest.predicate = NSPredicate(value: true)
self._players = FetchRequest(fetchRequest: fetchRequest)
do {
let firstPlayer = try moc.fetch(fetchRequest)
self._selection = State(initialValue: firstPlayer[0])
} catch {
fatalError("Uh, fetch problem...")
}
}
var body: some View {
VStack{
Picker("", selection: $selection){
ForEach(players) { (player: Player) in
Text(player.givenName ?? "")
.tag(player)
}
}
Text(selection.familyName ?? "No family name")
Text("\(players.count)")
}
}
}
SwiftUI - Using CoreData Fetched Result to Populate Picker
You need to make your value an optional String because that is what the type primaryKeyId is
@Binding var value: String?
And then you need a tag on each element of the picker to set the selection:
Text(data.value ?? "Unknown").tag(data.primaryKeyId)
Related Topics
Problem Creating and Writing a Subdirectory in Swift 5
How to Handle Async Requests in Swift
How to Handle Two Possible Date Formats
Xcode 7.1 Beta: Content of File Error
Unexpectedly Found Nil While Unwrapping an Optional Value While Reading from Ds with Fromcstring
Checkboxes in Uitableview State Persistence
How to Pass Arguments into a Function with Completion Swift
Where to Place App Delegate Code in App.Swift File
How to Notify a Queue in Swift (Gcd)
With Data (Not Nsdata), in Fact How Actually Do You Make a Utf8 Version of a Jpeg
Need Clarification for Swift Type Properties
Swiftui How to Invoke the Function and Change View from Other Page
Make Swiftui Rectangle Same Height or Width as Another Rectangle
Ios8: Auto-Layout and Gradient
Why Is My Sound Making My Game Lag in Swift Spritekit
Which Measuring Unit Is Used in Scnvector3 Position for X, Y and Z in Arkit