如何使用swiftui将文本字段存储到核心数据中

red*_*arz 2 core-data swift swiftui

我无法完成一项任务,在进入 时使用核心数据存储值TextField,并在进入视图时再次显示。是否可以?

我需要存储namesurname。为此,我创建了ProfileData数据模型,但找不到任何相关信息。关于如何使其正常工作。

请在代码下方找到:

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"))
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的 ProfileData+CoreClassData.swift:

import Foundation
import CoreData

@objc(ProfileData)
public class ProfileData: NSManagedObject {

}
Run Code Online (Sandbox Code Playgroud)

这是我的 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?

}
Run Code Online (Sandbox Code Playgroud)

Asp*_*pid 5

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

notice several things:

  1. FetchRequest returns you a list of entities of type you define. There may be one item, several items or there might be none.
  2. If you fetches something, you need to have ForEach loop to show the result (most of the time).
  3. To do so, I added an 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 updates
  4. Don't forget to change codegen 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.
  5. If you want to create new object of your type - you need to use MOC. Its the only way to create objects of NSManagedObject type.
  6. in TextField you can put a BindableObject. You tried to use @State for that purpose, but you don't need it. You can modify ordinary objects property just like that: 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.
  7. You can't just put @NSManaged property, because most of them have optional type like String?. I made a computed property wrappedName to use it simply in Views.
  8. I use an @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.
  9. There is a way to use Canvas even if you using CoreData. Just get the context from AppDelegate, create any test entities. But remember, saved changes will add to you're preview.
  10. And finally, you need to save all the changes with 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.

祝你学习 SwiftUI 好运。使用 SwiftUI 和 CoreData 的信息并不多。尝试在hackingwithswift上检查它 ,有很多有用的信息。