如何在 Core Data iOS Swift 中删除和更新结构类型数组? [英] How to delete and update structure type array in Core Data iOS Swift?

查看:18
本文介绍了如何在 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.

使用 classNSSecureCoding 是最好的方法.最灵活.

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

只需添加一个名为 lendingDataJSONattribute 类型 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆