Reputation: 135
I can't complete a task to store a value with core data when entered into TextField
, and to be showed again when entering the view. Is it possible?
I need to store name
and surname
. For that I created ProfileData
data model but can't find any relevant info. on how to make it work properly.
Please find below the code:
import SwiftUI
import CoreData
struct ProfileView: View {
@State private var name: String = ""
@State private var surname: String = ""
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(
entity: ProfileData.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \ProfileData.name, ascending: true),
NSSortDescriptor(keyPath: \ProfileData.surname, ascending: true),
]
) var profile: FetchedResults<ProfileData>
@EnvironmentObject var profile1: ProfileData
var body: some View {
VStack {
HStack {
VStack {
HStack {
Text("Meno:")
.font(.headline)
.padding()
TextField("", text: $name, onCommit: {
self.profile1.name = self.name
try? self.managedObjectContext.save()})
.onAppear {
self.name = self.profile.name != nil ? "\(self.profile.name!)" : "Zadajte meno" //here I get error Value of type 'FetchedResults<ProfileData>' has no member 'name'
}
.onDisappear {
self.profile1.name = self.name
try? self.managedObjectContext.save()
}
} .padding(.leading, 37)
}
HStack {
Text("Priezvisko:")
.font(.headline)
.padding()
TextField("Zadajte priezvisko", text: $surname)
}
}
}
}
.navigationBarTitle(Text("Profil"))
}
}
Here is my ProfileData+CoreClassData.swift:
import Foundation
import CoreData
@objc(ProfileData)
public class ProfileData: NSManagedObject {
}
And here is my ProfileData+CoreDataProperties.swifft
//
// ProfileData+CoreDataProperties.swift
// UcmMap
//
// Created by Jakub Adamec on 06/01/2020.
// Copyright © 2020 Jakub Adamec. All rights reserved.
//
//
import Foundation
import CoreData
extension ProfileData {
@nonobjc public class func fetchRequest() -> NSFetchRequest<ProfileData> {
return NSFetchRequest<ProfileData>(entityName: "ProfileData")
}
@NSManaged public var name: String?
@NSManaged public var surname: String?
}
Upvotes: 2
Views: 4740
Reputation: 699
Here's full code:
import SwiftUI
import CoreData
@objc(ProfileData)
public class ProfileData: NSManagedObject, Identifiable {
public var id = UUID()
}
extension ProfileData {
@NSManaged public var name: String?
public var wrappedName: String{
get{name ?? "NoName"}
set{name = newValue}
}
@NSManaged public var surname: String?
public var wrappedSurname: String{
get{surname ?? "NoSurname"}
set{surname = newValue}
}
}
struct ProfileView: View {
@State private var name: String = ""
@State private var surname: String = ""
@Environment(\.managedObjectContext) var moc: NSManagedObjectContext // it will need you to add new examples of youre entities and save all changes
@FetchRequest(
entity: ProfileData.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \ProfileData.name, ascending: true),
NSSortDescriptor(keyPath: \ProfileData.surname, ascending: true),
]
) var profileList: FetchedResults<ProfileData>
//fetchRequest is a list of all objects off type ProfileData - saved and unsaved
var body: some View {
NavigationView{
List{
ForEach(profileList){profile in
NavigationLink(destination: profileUpdateView(profile: profile)){
Text("\(profile.wrappedName) \(profile.wrappedSurname) ")
}
}
HStack{
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.imageScale(.large)
Button("add a new profile"){
let newProfile = ProfileData(context: self.moc)
newProfile.wrappedName = "Name"
newProfile.wrappedSurname = "Surname"
}
}
}
.navigationBarTitle(Text("Profile"))
.navigationBarItems(trailing: Button("save"){
if self.moc.hasChanges{
do{try self.moc.save()}
catch{print("Cant save changes: \(error)")}
}
})
}
}
}
struct profileUpdateView: View {
@ObservedObject var profile: ProfileData
var body: some View{
VStack {
HStack {
Text("Meno:")
.font(.headline)
.padding()
TextField("Zadajte meno", text: $profile.wrappedName)
}
HStack {
Text("Priezvisko:")
.font(.headline)
.padding()
TextField("Zadajte priezvisko", text: $profile.wrappedSurname)
}
}
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newProfile = ProfileData(context: context)
newProfile.wrappedName = "Name"
newProfile.wrappedSurname = "Surname"
return ProfileView().environment(\.managedObjectContext, context)
}
}
notice several things:
FetchRequest
returns you a list of entities of type you define.
There may be one item, several items or there might be none.ForEach
loop to show the result (most of the time).id
to you're entity. Its not connected to CoreData, and every time FetchRequest
gets new results, id will
change, but that's fine. The only purpose of it - is to let ForEach
know, which part of loop is connected with exactly object. Therefor
ForEach
can change it and show you updatescodegen
property of your entities in data model to manual/none
. To do so open DataModel like, select your entity and go to data model inspector (right side of the screen). If you don't the compiler will create those files in compilation itself and this class will be defined twice.NSManagedObject
type.TextField("Zadajte meno", text: $profile.wrappedName)
The $
symbol is to make this property
wrapped @Binding
. This means, all the changes made inside this
View
will translate into this object instantly.@NSManaged
property, because most of them have optional type like String?. I made a computed property
wrappedName
to use it simply in Views.@ObservedObject
wrapper in update View. Its like @State
, but for classes. you use it when you want instantly update the View
when this object changes. It also helps create a Binding
. Your class must meet the ObservableObject
protocol requirements, but NSManagedObject
already does, so you don't need to worry about it. All @NSManaged
attributes are already @Published
.AppDelegate
, create any test entities. But remember, saved changes will add to you're preview.moc.save()
. It can throw an exception, so you must do it in try-catch
. And its
a good practice to check, if there really are unsaved changes.Good luck in learning SwiftUI. There is not so much info of using SwiftUI and CoreData. Try to check it on hackingwithswift, there's much helpful information.
Upvotes: 7