【问题标题】:Save complex relational data in CoreData在 CoreData 中保存复杂的关系数据
【发布时间】:2021-11-05 09:46:41
【问题描述】:

我回到了使用 CoreData 的 SwiftUI 学习课程。我有三个实体:


User // Has many Customers
Customer // Belongs to User and has many PartExchanges
PartExchange // Belongs to customer

当用户在登录后首次安装应用程序时,我会获取一些要保存的初始数据(以上:客户、零件交换等...):


struct AuthResponse: Decodable {
    let error: String?
    let token: String?
    let userData: UserObject?
    let customers: [Customers]?

    struct UserObject: Decodable {
        let FirstName: String?
        let Surname: String?
        let EmailAddress: String?
        let UserID: String
    }
    
    struct Customers: Decodable {
        let FirstName: String?
        let Surname: String?
        let EmailAddress: String?
        let Customer_ID: String
        let PartExchanges: [PartExchangeData]?
    }
}

// In another file and not inside AuthResponse
struct PartExchangeData: Decodable {
    let Registration: String?
    let Customer_ID: String?
    let PartExchange_ID: String?
    let Variant: String?
    let Colour: String?
}

AuthResponse 仅在用户首次登录或重新安装应用以从我们的 API 获取初始数据时使用:


// The exact data I have

import SwiftUI


class AuthController {
    
    var emailUsername: String = ""
    var password: String = ""
    
    
    func login() -> Void {
        
        guard let url = URL(string: "http://localhost:4000/api/auth") else {
            print("Invalid URL")
            return
            
        }
        
        var request = URLRequest(url: url)
        
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let body: [String: AnyHashable] = [
            "emailUsername": emailUsername,
            "password": password
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
        
        // Make the request
        URLSession.shared.dataTask(with: request) { data, response, error in
            
            if let data = data {
                
                let decoder = JSONDecoder()
                
                decoder.dateDecodingStrategy = .iso8601
                
                if let decodedResponse = try?
                    decoder.decode(AuthResponse.self, from: data) {
                    DispatchQueue.main.async {
                        
                        if decodedResponse.error != nil {
                            // Tell user?
                            return
                        }
                        
                        let userObject = UserModel()
                        userObject.createUser(authObject: decodedResponse)
                        
                    }
                    
                    return
                }
                
            }
            print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
            
        }.resume()
    }
    
}

最后,UserModel

class UserModel: ObservableObject {
    private let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
    private let viewContext = PersistenceController.shared.container.viewContext
    
    @Published var saved: Bool = false
    
    var firstName: String = ""
    var surname: String = ""
    var emailAddress: String = ""
    var token: String = ""
    var userId: String = ""
    
    init() {...}
    
    public func createUser(authObject: AuthResponse) -> Void {
        
        do {
            // Create a user on first login
            let user = User(context: viewContext)
            let customer = Customer(context: viewContext)
            let partExchange = PartExchange(context: viewContext)
            //let userCustomers: [AuthResponse.Customers]
            
            user.firstName = authObject.userData!.FirstName
            user.surname = authObject.userData!.Surname
            user.emailAddress = authObject.userData!.EmailAddress
            user.token = authObject.token!
            user.userId = authObject.userData!.UserID
            
            
            
            // Save customers
            for cus in authObject.customers! {
                customer.firstName = cus.FirstName
                customer.surname = cus.Surname
                
                user.addToCustomers(customer)
                
                // save part exchanges
                for px in cus.PartExchanges! {
                    
                    partExchange.registration = px.Registration
                    partExchange.partExchangeId = px.PartExchange_ID
                    partExchange.variant = px.Variant
                    
                    customer.addToPartExchanges(partExchange)
                    
                }
                
            }

            
            try viewContext.save()
            
            saved = true
            print("ALL SAVED!!")
        
        } catch {
            let error = error as NSError
            // If any issues, rollback? viewContext.rollback()
            fatalError("Could not save user: \(error)")
        }
        
    }
    
    public func logOut() {
        // Only remove the token....
    }  
}

我在使用这种方法时遇到的问题是保存时;它正在保存循环中的最后一个客户。

Xcode 为UserCustomerPartExchnage 生成了一些扩展,在User 内部,我看到了一个函数:@NSManaged public func addToCustomers(_ values: NSSet)


[..]

user.addToCustomers(<what-goes-here>)

我的用户实体正确保存。客户只有 api 数组中的最后一个数据。如何正确保存客户多的用户,每个客户有很多零件交换?

【问题讨论】:

  • 您需要为每个循环中的每次迭代创建一个新对象,您不能重用同一个对象,因为这意味着您只是在更新它,并且只存储上次迭代的数据。所以将let customer = Customer(…let partExchange = PartExchange(… 移动到相应循环的开头
  • 啊,好吧...所以我应该在每个实体的循环中都有try viewContext.save()
  • 不需要,它会保存所有插入的对象。
  • 我想我明白了。让我重构然后再回复你。谢谢
  • @JoakimDanielson 你是明星!!您可以创建一个答案,我会接受它。只取决于你。再次感谢您!

标签: swift core-data


【解决方案1】:

您需要为每个循环中的每次迭代创建一个新对象,因为创建的每个对象都将作为单独的项目存储在 Core Data 中

所以把createUser改成这样

public func createUser(authObject: AuthResponse) -> Void {        
    do {
        let user = User(context: viewContext)                       
        user.firstName = authObject.userData!.FirstName
        // more properties ...          
        
        for cus in authObject.customers! {
            let customer = Customer(context: viewContext)
            customer.firstName = cus.FirstName
            customer.surname = cus.Surname
            
            user.addToCustomers(customer)
            
            for px in cus.PartExchanges! {
                let partExchange = PartExchange(context: viewContext)
                partExchange.registration = px.Registration
                partExchange.partExchangeId = px.PartExchange_ID
                partExchange.variant = px.Variant
                
                customer.addToPartExchanges(partExchange)                    
            }                
        }
        
        try viewContext.save()
        
        saved = true
        print("ALL SAVED!!")
    
    } catch let error = error as NSError {
        //Either log the error and return some status or throw it
        //FatalError is a bit to much in this situation          
        fatalError("Could not save user: \(error)")
    }
    
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 2017-08-04
    相关资源
    最近更新 更多