如何在 Core Data iOS Swift 中删除和更新结构类型数组? [英] How to delete and update structure type array in Core Data iOS Swift?
问题描述
如何在 Core Data iOS Sswift 中删除和更新结构类型数组?我正在像这样保存到核心数据.我需要删除和更新包含值的选定单元格
How to delete and update structure type array in Core Data iOS Sswift? I am saving to core data like this. I need to delete and update selected cell containing values
let projectsInfo = NSEntityDescription.insertNewObject(forEntityName:"ItemsInfo", into: delegate.persistentContainer.viewContext) as! ItemsInfo
let auditArray:[String:[lendingData]] = ["allcreditData":SaveWitnessData.shared.LendingDataArray]
let jsonData = try! JSONEncoder().encode(auditArray) projectsInfo.values = jsonData
delegate.saveContext()
我的结构就像
struct lendingData : Codable {
let userName : String
let amount : String
let date : String
let type : String
var witnessDetails : [witnessData]
}
推荐答案
选项 1.
使用 class
和 NSSecureCoding
是最好的方法.最灵活.
Using a class
and NSSecureCoding
is the best way of doing this. The most flexible.
///To See the whole thing in action you have to follow a few steps
///Step 1. Create an new SwiftUI project with CoreData
///Step 2. Copy all the code in Option 1 into a `.swift` file
///Step 3. Go to the `Persistence.swift` file
/// Place these 2 lines
/// `WitnessDataTransformer.register()`
/// `LendingDataTransformer.register()`
/// Just under `container = NSPersistentCloudKitContainer(name: "YourAppName")
///Step 4. Go to the CoreData model
/// Select the `Item` Entity
/// Add a `lendingData` attribute of type `Transformable`
/// Update the `Transformer` and `Custom Class` in the `Data Model Inspector` as shown
///Step 5. You should see the View on Canvas in this point
第 4 步的照片
代码
import SwiftUI
//struct and class should start with an uppercase
//You need secureCoding not codable
//You have to change to class because NSSecurecoding is not available for a struct -https://developer.apple.com/documentation/foundation/nssecurecoding
public class LendingData : NSObject, Identifiable, ObservableObject{
public let id: String
@Published var userName : String
@Published var amount : String
@Published var date : String
@Published var type : String
//WitnessData needs to conform to secure coding as well
@Published var witnessDetails : [WitnessData]
static func sample() -> LendingData {
LendingData(id: UUID().uuidString, userName: "sample name", amount: "10.00", date: "(Date())", type: "sample type", witnessDetails: [WitnessData.sample(), WitnessData.sample()])
}
static func blank() -> LendingData {
LendingData(id: UUID().uuidString, userName: "", amount: "", date: "", type: "", witnessDetails: [])
}
public enum CodingKeys: String, CodingKey {
case id
case userName
case amount
case date
case type
case witnessDetails
}
public init(id: String, userName : String, amount : String, date : String, type : String, witnessDetails : [WitnessData]) {
self.id = id
self.userName = userName
self.amount = amount
self.date = date
self.type = type
self.witnessDetails = witnessDetails
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as! String
userName = coder.decodeObject(forKey: CodingKeys.userName.rawValue) as! String
amount = coder.decodeObject(forKey: CodingKeys.amount.rawValue) as! String
date = coder.decodeObject(forKey: CodingKeys.date.rawValue) as! String
type = coder.decodeObject(forKey: CodingKeys.type.rawValue) as! String
witnessDetails = coder.decodeArrayOfObjects(ofClass: WitnessData.self, forKey: CodingKeys.witnessDetails.rawValue) ?? []
}
}
extension LendingData: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(userName, forKey: CodingKeys.userName.rawValue)
coder.encode(amount, forKey: CodingKeys.amount.rawValue)
coder.encode(date, forKey: CodingKeys.date.rawValue)
coder.encode(type, forKey: CodingKeys.type.rawValue)
coder.encode(witnessDetails, forKey: CodingKeys.witnessDetails.rawValue)
}
}
///MUST CALL LendingDataTransformer.register() right after creating the Persistent Container before setup and loading store
@objc(LendingDataTransformer)
public final class LendingDataTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: LendingDataTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [LendingData.self, NSString.self, NSArray.self, WitnessData.self]
}
//Register before CoreData setup starts
@objc dynamic
public static func register() {
let transformer = LendingDataTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
//You have to change to class because NSSecurecoding is not available for a struct -https://developer.apple.com/documentation/foundation/nssecurecoding
public class WitnessData: NSObject, Identifiable, ObservableObject{
public let id: String
//This is just a sample since you did not provide the struct
//Add your variables to
// the class,
// the CodingKeys,
// init?(coder: NSCoder),
// encode(with coder: NSCoder), and
// init(id: String, name : String).
// Just follow the pattern.
@Published var name: String
static func sample() -> WitnessData{
WitnessData(id: UUID().uuidString, name: UUID().uuidString)
}
static func blank() -> WitnessData{
WitnessData(id: UUID().uuidString, name: "")
}
public enum CodingKeys: String, CodingKey {
case id
case name
}
public init(id: String, name : String) {
self.id = id
self.name = name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? String ?? ""
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String ?? ""
}
}
extension WitnessData: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
///MUST CALL WitnessDataTransformer.register() right after creating the Persistent Container before setup and loading store
@objc(WitnessDataTransformer)
public final class WitnessDataTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: WitnessDataTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [WitnessData.self, NSString.self, NSArray.self]
}
//Register before CoreData setup starts
@objc dynamic
public static func register() {
let transformer = WitnessDataTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
以下 SwiftUI 代码适用于选项 1 或选项 2
The below SwiftUI code works for both option 1 or option 2
///This is just a sample View
struct LendingDataView: View {
//You will need the original ObservableObject if you want to be able to show changes
//SwiftUI depends on being told that there are chagnes so it can reload Views
@ObservedObject var item: Item
var body: some View {
if item.lendingData != nil{
List{
TextField("username",text: $item.lendingData.bound.userName)
TextField("amount",text: $item.lendingData.bound.amount)
TextField("date",text: $item.lendingData.bound.date)
TextField("type",text: $item.lendingData.bound.type)
Section(content: {
ForEach($item.lendingData.bound.witnessDetails, content: { $witness in
HStack{
TextField("name",text: $witness.name)
Spacer()
//For deleting by object
Image(systemName: "trash")
.foregroundColor(.red)
.onTapGesture {
let idx = item.lendingData!.witnessDetails.firstIndex(where: {
$0.id == witness.id
})
if idx != nil{
item.lendingData!.witnessDetails.remove(at: idx!)
}
//Because you are so far down the line you have to tell the ObservableObject there is a change
//If you dont you won't see the new items until something happens to trigger a refresh
//item.objectWillChange.send()
item.objectWillChange.send()
}
}
})
//For deleting by index
.onDelete(perform: { indexSet in
for idx in indexSet{
item.lendingData!.witnessDetails.remove(at: idx)
}
})
}, header: {
HStack{
Text("Witness Data")
Button(action: {
item.lendingData!.witnessDetails.append(WitnessData.blank())
//Because you are so far down the line you have to tell the ObservableObject there is a change
//If you dont you won't see the new items until something happens to trigger a refresh
item.objectWillChange.send()
}, label: {
Image(systemName: "plus")
})
}
})
}
}else{
VStack{
Text("no lending data")
Button(action: {
item.lendingData = LendingData.blank()
}, label: {
Image(systemName: "plus")
})
}
}
}
}
//Standard Preview
struct LendingDataView_Previews: PreviewProvider {
//Use the preview container
static let context = PersistenceController.preview.container.viewContext
static var sampleItem = Item(context: context)
static var previews: some View {
LendingDataView(item: sampleItem)
}
}
extension Optional where Wrapped == LendingData {
var _bound: LendingData? {
get {
return self
}
set {
self = newValue
}
}
var bound: LendingData {
get {
return _bound ?? LendingData.blank()
}
set {
_bound = newValue
}
}
}
就像我一开始说的,class
是最安全的方法,但您可以使用 struct
.
Like I said at the start class
is the safest way but you can use the struct
.
选项 2
只需添加一个名为 lendingDataJSON
的 attribute
类型 String?
INSTEAD 的 lendingData
类型的 >可转换
Just add an an attribute
named lendingDataJSON
of Type String?
INSTEAD of the lendingData
of type Transformable
struct LendingData : Codable, Identifiable{
let id: String
var userName : String
var amount : String
var date : String
var type : String
var witnessDetails : [WitnessData]
static func sample() -> LendingData {
LendingData(id: UUID().uuidString, userName: "sample name", amount: "10.00", date: "(Date())", type: "sample type", witnessDetails: [WitnessData.sample(), WitnessData.sample()])
}
static func blank() -> LendingData {
LendingData(id: UUID().uuidString, userName: "", amount: "", date: "", type: "", witnessDetails: [])
}
}
struct WitnessData: Codable, Identifiable{
let id: String
var name: String
static func sample() -> WitnessData{
WitnessData( id: UUID().uuidString, name: UUID().uuidString)
}
static func blank() -> WitnessData{
WitnessData( id: UUID().uuidString, name: "")
}
}
//The App's CoreData Model will need an attibute
// named lendingDataJSON of Type String
extension Item{
//This computed property should be the only way that the app alters the LendingData
//If you use the lendingDataJSON directly you can corrupt all of it
var lendingData: LendingData?{
get{
let decoder = JSONDecoder()
if let obj = try? decoder.decode(LendingData.self, from: self.lendingDataJSON?.data(using: .utf8) ?? Data()) {
return obj
}else{
return nil
}
}
set{
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let encoded = try? encoder.encode(newValue) {
self.lendingDataJSON = String(data: encoded, encoding: .utf8) ?? ""
}
}
}
}
所有 View
代码将与 class
选项或 struct
选项一起工作
All the View
code will work the same with the class
option or with the struct
option
这篇关于如何在 Core Data iOS Swift 中删除和更新结构类型数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!