Core Data observed object in SwiftUI - core-data

I have created a list of clients which get saved in core data. I added some attributes through an AddView and now I am trying to add a new attribute using a different view. In order to do that I understood I need to use the observedObject property wrapper so that it doesn't create a new client but update the existing one. This is the code:
import SwiftUI
import CoreData
struct TopicView: View {
#Environment(\.managedObjectContext) var managedObjectContext
let myStudent: StudentData
#ObservedObject var topic: StudentData
#State var content = ""
#State private var show = false
var body: some View {
Section (header: Text("Topics covered")
.bold()
.padding(.all)
.font(.title3)) {
TextField("Add content", text: $topic.content ?? "") //error here
//Text(topic.content ?? "")
.padding()}
Button ("Save")
{ let newStudent = StudentData(context: self.managedObjectContext)
newStudent.content = self.topic.content
try? self.managedObjectContext.save()
self.topic.content = ""
}.foregroundColor(.blue)
.cornerRadius(10)
.frame(width: 200, height: 50, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.font(.headline)
Spacer()
}
}
I get two errors saying:
Cannot convert value of type 'Binding<String?>' to expected argument type 'Binding'
and
Cannot convert value of type 'String' to expected argument type 'Binding<String?>'
It might be an easy fix but as I am still learning I can't get my head around it.
Thanks

Thank you all,
This is what I found and seems to work:
TextField("Add content", text: Binding(
get: { self.topic.content ?? ""},
set: { self.topic.content = $0 } )
)

Related

How to use a function in another swift file that contains CoreData related properties?

clueless beginner here. Apologies if my question is formed poorly. This is the first time I ask a question of this scale and I have a hard time balancing between posting too much code for the good samaritans to read and too little to post an effective question. Huge thanks in advance!
I am trying to incorporate the textfieldalert in this post in my learner project. There are two Swift files in questions: File A (PosTextFieldAlertView) has an extension that needs to use two functions in File B (ListView).
These are the functions I need to use in File A.
func addPositive(){
let newPositive = PositiveEntity(context: viewContext)
newPositive.title = alertInput
save()
}
func save() {
do { try viewContext.save() } catch { print(error) }
}
I thought of/researched two methods: 1) duplicate the function in File A or 2) create instance of the view in File B that contains that functions according this post. However I ran into problems in both methods.
Duplicating the functions:
I copied the CoreData related properties in the PosTextFieldAlert struct. But now PosTextFieldAlert in the return part of the extension has the error of "Missing arguments for parameters [Core Data properties] in call". I don’t know how to set the property in the extension without referring or creating a different sets of Core Data entities.
Creating an instance of the relevant view
In the instance creation I would need to input the arguments but I don’t know how to refer to the same NSManagedObjectContext.
Code excerpts:
PosTextFieldAlertView
struct PosTextFieldAlert<Presenting>: View where Presenting: View {
var viewContext: NSManagedObjectContext
var positives: [PositiveEntity]
var targets: [TargetEntity]
#State private var alertInput = ""
// let listView = ListView(viewContext: NSManagedObjectContext, positives: PositiveEntity, negatives: NegativeEntity, targets: TargetEntity)
#Binding var isShowing: Bool
#Binding var text: String
let presenting: Presenting
let title: String
var body: some View {
GeometryReader { (deviceSize: GeometryProxy) in
ZStack {
self.presenting
.disabled(isShowing)
VStack {
Text(self.title)
TextField(self.title, text: self.$text)
Divider()
VStack{
HStack {
Button(action: {
withAnimation {
self.isShowing.toggle()
}
}) {
Text("+")
}.padding()
Button(action: {
withAnimation {
self.isShowing.toggle()
}
}) {
Text("-")
}.padding()
}
Button(action: {
withAnimation {
self.isShowing.toggle()
}
}) {
Text("Done")
}
}
}
.padding()
.background(Color.white)
.frame(
width: deviceSize.size.width*0.7,
height: deviceSize.size.height*0.7
)
.shadow(radius: 1)
.opacity(self.isShowing ? 1 : 0)
}
}
}
func addPositive(){
let newPositive = PositiveEntity(context: viewContext)
newPositive.title = alertInput
save()
}
func save() {
do { try viewContext.save() } catch { print(error) }
}
}
extension View {
func posTextFieldAlert(isShowing: Binding<Bool>,
text: Binding<String>,
title: String) -> some View {
PosTextFieldAlert(isShowing: isShowing,
text: text,
presenting: self,
title: title)
}
}
The code in ListView
struct ListView: View {
var viewContext: NSManagedObjectContext
var positives: [PositiveEntity]
var negatives: [NegativeEntity]
var targets: [TargetEntity]
//[layout of the project]
}
The Fetchrequests in ContentView:
#Environment(\.managedObjectContext) var viewContext
#FetchRequest(sortDescriptors: []) var targets: FetchedResults<TargetEntity>
#FetchRequest(sortDescriptors: []) var positives: FetchedResults<PositiveEntity>
#FetchRequest(sortDescriptors: []) var negatives: FetchedResults<NegativeEntity>

swiftUI - how to pass core data values of an item from ForEach list in NavigationLink to Detail view

I just cannot figure out how to pass core data values of an item from ForEach list in NavigationLink to Detail view. Here is the code that got error: "Cannot convert value of type 'FetchedResults.Element' (aka 'FileEnt') to expected argument type 'FileViewModel'"
struct FileList: View {
#Environment(\.managedObjectContext) var context
#FetchRequest(entity: FileEnt.entity(), sortDescriptors: [NSSortDescriptor(key: "fileName", ascending: false)]) var results: FetchedResults<FileEnt>
#ObservedObject var fileData : FileViewModel
var body: some View {
NavigationView {
List {
ForEach(results) { aFile in
NavigationLink(destination: FileDetails(fileData: aFile), label: {
// ** error on red-underscored aFile above.
Text(aFile.fileName ?? "")
})
}
}.navigationBarTitle("Files")
}
}
}
FileViewModel is for Add, Edit and Detail views. Here is its simplified version for the question:
class FileViewModel: ObservableObject {
#Published var fileName = ""
init(){
}
func DetailItem(fileItem: FileEnt){
fileName = fileItem.fileName ?? ""
}
}
FileDetails:
struct FileDetails: View {
#ObservedObject var fileData : FileViewModel
#Environment(\.managedObjectContext) var context
#State var isEdit = false
var body: some View {
VStack {
Form {
HStack {
Text("File Name:")
Spacer()
Text(fileData.fileName)
....
Modified the post for easy understanding. Thanks for any advice.

Would like to store system settings in core data

Using SwiftUI
I have created a CoreData Entity called SystemSettings and would like to use the information stored in it for calculating results in different functions on different views without using ForEach. SystemSettings will always have only one object (Record) stored from preloaded database. Is there a way I can use #FetchRequest and pull out the only object and its attributes out, as a var.... ?
Thank you
import SwiftUI
import CoreData
struct NewComponent: View {
#FetchRequest(entity: SystemSettings.entity(), sortDescriptors: []) var settings: FetchedResults<SystemSettings>
#Environment(\.managedObjectContext) var moc
#Environment(\.presentationMode) var presentationMode
var calculator = Calculator()
// USER DATA TEXT FIELDS
#State var parameterOne = "12.5"
#State var parameterTwo = "2.5"
#State var calculationResult = ""
var body: some View {
VStack {
Form {
Section (header: Text("Parameters").font(.headline).bold().italic()) {
HStack {
Text ("Length")
.font(.headline)
Spacer()
TextField ("m", text: $parameterOne).modifier(ClearButton(text: $parameterOne))
.multilineTextAlignment(.trailing)
.keyboardType(.decimalPad)
.font(.headline)
}
HStack {
Text ("Width")
.font(.headline)
Spacer()
TextField ("m", text: $parameterTwo).modifier(ClearButton(text: $parameterTwo))
.multilineTextAlignment(.trailing)
.keyboardType(.decimalPad)
.font(.headline)
}
}
}
Section(header:
Text("\(self.calculationResult)").bold().italic().padding(.top)
) {
Button(action: {
self.calculationResult = self.result()
}) {
HStack {
Spacer()
Image(systemName: "x.squareroot")
Text("Calculate!")
Spacer()
}.font(.headline).foregroundColor(Color.green)
.padding(.bottom, 40.0).padding(.top, 20.0)
}
}
}.navigationBarTitle(Text("Component Calculator"))
.gesture(DragGesture().onChanged{_ in UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)})
.modifier(KeyboardObserving())
.navigationBarItems(trailing: Button(action: {
let newEstimatedComponent = EstimatedComponents(context: self.moc)
newEstimatedComponent.name = "New Component"
newEstimatedComponent.price = self.priceToDisplay()
try? self.moc.save()
self.presentationMode.wrappedValue.dismiss()
}){Image(systemName: "checkmark.circle").font(.system(size: 30))})
}
func result () -> String {
let result = componentCost()
return (String(format: "%.2f",(result)) + " €")
}
func priceToDisplay() -> Double {
return componentCost()
}
func componentCost () -> Double {
return calculator (componentWidth: Double(parameterOne), componentHight: Double(parameterTwo), componentPrice: settings!.componentM2Price )
}
/*settings!componentM2Price not working of course, so I need to get the first and the only object from SystemSettings Entity in a way that I can use it in func componentCost()...*/
THANKS AGAIN...
Solved!
#EnvironmentObject var settings: ModifySystemSettings
let systemSettings: SystemSettings
You can find more information here:
https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

TextField Xcode 11 Binding String problem

I have a file called PDFManager , which is in charge to create and save the pdf file.
on PDFManager I create the var nameCPT : String = "" and from the ContentView i'm try to fill this var with a value from a textfield
problem is, Xcode give me the error say "Cannot convert value of type 'String' to expected argument type 'Binding'"
I tried on pdf manager create a var with #State but still not working
any idea how to pass the value of my textField to PDFManager?
thanks
import SwiftUI
struct ContentView: View {
var lm : PDFManager
var body: some View {
VStack {
fakebar
Spacer()
HStack {
TextField("Insert Nome CPT", text: lm.nameCPT).
// not working, I try with $lm.nameCPT but still not working
.padding(.leading)
}
Spacer()
}
}
You need to use #Observed property wrapper to allow the property being observed and comply with ObservableObject in your PDFManager class.
class PDFManager:ObservableObject {
var nameCPT:String = "test"
}
struct ContentView: View {
#ObservedObject var lm : PDFManager = PDFManager()
var body: some View {
VStack {
Spacer()
HStack {
TextField("Insert Nome CPT", text: $lm.nameCPT)
.padding(.leading)
Button(action:{
// Prints the value stored in your PDF manager
print(self.lm.nameCPT)
}){
Text("Check")
}
}
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

SwiftUI TextField CoreData - Changing an attribute's data

I'm trying to use TextField to change the data of an attribute of CoreData, and everything I've come up with hasn't been successful. There is a similar question (listed below), and I'm going to post the code from the correct answer to that to explain it.
struct ItemDetail: View {
#EnvironmentObject var itemStore: ItemStore
let idx: Int
var body: some View {
NavigationView {
Stepper(value: $itemStore.items[idx].inventory) {
Text("Inventory is \(self.itemStore.items[idx].inventory)")
}
// Here I would like to do this
// TextField("PlaceHolder", $itemStore.items[idx].name)
// That doesn't work... also tried
// TextField("PlaceHolder", $name) - where name is a #State String
// How can you then automaticlly assign the new value of #State name
// To $itemStore.items[idx].name?
.padding()
.navigationBarTitle(itemStore.items[idx].name)
}
}
}
Original Question:
SwiftUI #Binding doesn't refresh View
I now have it working.
struct ItemDetail: View {
#EnvironmentObject var itemStore: ItemStore
let idx: Int
// Added new #State variable
#State var name = ""
var body: some View {
NavigationView {
Stepper(value: $itemStore.items[idx].inventory) {
Text("Inventory is \(self.itemStore.items[idx].inventory)")
}
TextField("Placeholder", text: $name) {
// When the enter key is tapped, this runs.
self.itemStore.items[self.idx].name = self.name
}
.padding()
.navigationBarTitle(itemStore.items[idx].name)
}
}
}

Resources