在 CoreData 中保存复杂的关系数据
Posted
技术标签:
【中文标题】在 CoreData 中保存复杂的关系数据【英文标题】:Save complex relational data in 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 为User
、Customer
和PartExchnage
生成了一些扩展,在User
内部,我看到了一个函数:@NSManaged public func addToCustomers(_ values: NSSet)
:
[..]
user.addToCustomers(<what-goes-here>)
我的用户实体正确保存。客户只有 api 数组中的最后一个数据。如何正确保存客户多的用户,每个客户有很多零件交换?
【问题讨论】:
您需要为每个循环中的每次迭代创建一个新对象,您不能重用同一个对象,因为这意味着您只是在更新它,并且只存储上次迭代的数据。所以将let customer = Customer(…
和let partExchange = PartExchange(…
移动到相应循环的开头
啊,好吧...所以我应该在每个实体的循环中都有try viewContext.save()
?
不需要,它会保存所有插入的对象。
我想我明白了。让我重构然后再回复你。谢谢
@JoakimDanielson 你是明星!!您可以创建一个答案,我会接受它。只取决于你。再次感谢您!
【参考方案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)")
【讨论】:
以上是关于在 CoreData 中保存复杂的关系数据的主要内容,如果未能解决你的问题,请参考以下文章