如何使用SwiftUI将文本字段存储到Core Data中。

huangapple go评论88阅读模式
英文:

How to store textfield into core data with swiftui

问题

I can help you with the translation. Here's the translation of the code portion you provided:

我无法完成一个任务,将值存储到Core Data中,当输入到`TextField`中时,再次进入视图时显示出来。这可能吗?

我需要存储“名字”和“姓氏”。为此,我创建了“ProfileData”数据模型,但找不到任何相关信息,说明如何使其正常工作。

请查看下面的代码:

```swift
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.first?.name ?? "Zadajte meno"
                        }
                        .onDisappear {
                            self.profile1.name = self.name
                            try? self.managedObjectContext.save()
                        }
                    }
                    HStack {
                        Text("Priezvisko:")
                            .font(.headline)
                            .padding()
                        TextField("Zadajte priezvisko", text: $surname)
                    }
                }
            }
        }
        .navigationBarTitle(Text("Profil"))
    }
}

这是我的ProfileData+CoreClassData.swift:

import Foundation
import CoreData

@objc(ProfileData)
public class ProfileData: NSManagedObject {

}

这是我的ProfileData+CoreDataProperties.swift:

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?

}

如果您有任何其他疑问,请告诉我。

英文:

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 = &quot;&quot;
    @State private var surname: String = &quot;&quot;
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: ProfileData.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \ProfileData.name, ascending: true),
            NSSortDescriptor(keyPath: \ProfileData.surname, ascending: true),
            
        ]
    ) var profile: FetchedResults&lt;ProfileData&gt;
    @EnvironmentObject var profile1: ProfileData
    
    var body: some View {
        VStack {
            HStack {
                VStack {
                    HStack {
                        Text(&quot;Meno:&quot;)
                            .font(.headline)
                            .padding()
                        TextField(&quot;&quot;, text: $name, onCommit: {
                        self.profile1.name = self.name
                        try? self.managedObjectContext.save()})
                    .onAppear {
                        self.name = self.profile.name != nil ? &quot;\(self.profile.name!)&quot; : &quot;Zadajte meno&quot; //here I get error Value of type &#39;FetchedResults&lt;ProfileData&gt;&#39; has no member &#39;name&#39;
                    }
                    .onDisappear {
                        self.profile1.name = self.name
                        try? self.managedObjectContext.save()
                    }
                        }   .padding(.leading, 37)
                    }
                    HStack {
                        Text(&quot;Priezvisko:&quot;)
                            .font(.headline)
                            .padding()
                        TextField(&quot;Zadajte priezvisko&quot;, text: $surname)
                    }
                }
            }
        }
        .navigationBarTitle(Text(&quot;Profil&quot;))
    }
}

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 &#169; 2020 Jakub Adamec. All rights reserved.
//
//

import Foundation
import CoreData


extension ProfileData {

    @nonobjc public class func fetchRequest() -&gt; NSFetchRequest&lt;ProfileData&gt; {
        return NSFetchRequest&lt;ProfileData&gt;(entityName: &quot;ProfileData&quot;)
    }

    @NSManaged public var name: String?
    @NSManaged public var surname: String?

}

答案1

得分: 7

以下是翻译好的部分:

这是完整的代码:

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)
    }
}

注意以下几点:

1. `FetchRequest` 返回您定义类型的实体列表。可能会有一个项目,几个项目,或者可能没有。
2. 如果您获取了数据,您需要使用 `ForEach` 循环来显示结果(大多数情况下)。
3. 为了实现这一点,我为您的实体添加了一个 `id`。它与 CoreData 无关,每次 `FetchRequest` 获取新结果时,`id` 都会更改,但这没关系。它的唯一目的是让 `ForEach` 知道哪一部分循环与具体对象相关联。因此,`ForEach` 可以进行更改并显示更新。
4. 不要忘记将数据模型中实体的 `codegen` 属性更改为 `manual/none`。要做到这一点,打开数据模型,选择您的实体,然后转到数据模型检查器(屏幕右侧)。如果不这样做,编译器将在编译过程中创建这些文件,这个类将被定义两次。
5. 如果要创建您类型的新对象,您需要使用 MOC。这是创建 `NSManagedObject` 类型对象的唯一方法。
6.  TextField 中,您可以放置一个 `@Binding`。您尝试使用 `@State` 来实现这个目的,但您不需要它。您可以像这样简单地修改普通对象的属性:`TextField("Zadajte meno", text: $profile.wrappedName)` `$` 符号用于将该属性包装为 `@Binding`。这意味着在此视图内进行的所有更改都会立即传递到此对象。
7. 您不能只放置 `@NSManaged` 属性,因为它们大多数具有可选类型,例如 String?。我创建了一个计算属性 `wrappedName`,以便在视图中简单使用它。
8. 在更新视图中使用了 `@ObservedObject` 包装器。它类似于 `@State`,但用于类。当此对象更改时,您可以使用它来立即更新视图。它还有助于创建 `Binding`。您的类必须满足 `ObservableObject` 协议的要求,但 `NSManagedObject` 已经满足了这个要求,所以您不需要担心。所有 `@NSManaged` 属性已经是 `@Published`
9. 即使在使用 CoreData 时,也有一种方法可以使用 Canvas。从 `AppDelegate` 中获取上下文,创建任何测试实体。但请记住,保存的更改将添加到您的预览中。
10. 最后,您需要使用 `moc.save()` 保存所有更改。它可能会抛出异常,因此您必须在 `try-catch` 中执行它。检查是否确实有未保存的更改是一个良好的做法。

祝您在学习 SwiftUI 时好运。关于在 SwiftUI 中使用 CoreData 的信息并不太多。尝试在 [hackingwithswift][1] 上查找,那里有很多有用的信息。

[1]: https://www.hackingwithswift.com/quick-start/swiftui/introduction-to-using-core-data-with-swiftui
英文:

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 ?? &quot;NoName&quot;}
set{name = newValue}
}
@NSManaged public var surname: String?
public var wrappedSurname: String{
get{surname ?? &quot;NoSurname&quot;}
set{surname = newValue}
}
}
struct ProfileView: View {
@State private var name: String = &quot;&quot;
@State private var surname: String = &quot;&quot;
@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&lt;ProfileData&gt;
//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(&quot;\(profile.wrappedName) \(profile.wrappedSurname) &quot;)
}
}
HStack{
Image(systemName: &quot;plus.circle.fill&quot;)
.foregroundColor(.green)
.imageScale(.large)
Button(&quot;add a new profile&quot;){
let newProfile = ProfileData(context: self.moc)
newProfile.wrappedName = &quot;Name&quot;
newProfile.wrappedSurname = &quot;Surname&quot;
}
}
}
.navigationBarTitle(Text(&quot;Profile&quot;))
.navigationBarItems(trailing: Button(&quot;save&quot;){
if self.moc.hasChanges{
do{try self.moc.save()}
catch{print(&quot;Cant save changes: \(error)&quot;)}
}
})
}
}
}
struct profileUpdateView: View {
@ObservedObject var profile: ProfileData
var body: some View{
VStack {
HStack {
Text(&quot;Meno:&quot;)
.font(.headline)
.padding()
TextField(&quot;Zadajte meno&quot;, text: $profile.wrappedName)
}
HStack {
Text(&quot;Priezvisko:&quot;)
.font(.headline)
.padding()
TextField(&quot;Zadajte priezvisko&quot;, 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 = &quot;Name&quot;
newProfile.wrappedSurname = &quot;Surname&quot;
return ProfileView().environment(\.managedObjectContext, context)
}
}

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(&quot;Zadajte meno&quot;, 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.

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.

huangapple
  • 本文由 发表于 2020年1月6日 23:57:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/59615175.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定