在我的应用程序中,我尝试保存团队对象。
为了保存这个对象,我创建了一个带有add form和Save按钮的工作表,这两个视图共享相同的ViewModel,后者在工作表的父视图中初始化。
在我的逻辑中,当我继续保存以刷新视图并在列表中查看我的Teams时,我获取保存在数据库中的Teams。
在此之前,没有问题。但是,当我关闭应用程序并重新启动时,如果我回到我的团队列表,则没有显示任何项目
我试着用各种可能的方式调试这个问题,我没有看到任何异常,因为我知道我在几个应用程序中有相同的CoreData逻辑实现,而这些应用程序没有遇到这个问题。
核心数据堆栈:
class CoreDataStack {
// MARK: - Singleton
static let shared: CoreDataStack = CoreDataStack(modelName: "Boxscore")
var persistentContainer: NSPersistentContainer
// MARK: - Variables
var mainContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
// MARK: - Init
init(modelName: String, persistentStoreDescription: String? = "/dev/null") {
let description = NSPersistentStoreDescription()
if let persistentStoreDescription {
description.url = URL(fileURLWithPath: persistentStoreDescription)
}
persistentContainer = NSPersistentContainer(name: modelName)
persistentContainer.persistentStoreDescriptions = [description]
persistentContainer.loadPersistentStores(completionHandler: { (_, error) in
guard let unwrappedError = error else { return }
fatalError("Unresolved error \(unwrappedError.localizedDescription)")
})
}
}
核心数据管理器:
class CoreDataManager {
enum CDErrors: Error {
case noData
case saveError
}
let managedObjectContext: NSManagedObjectContext
init(managedObjectContext: NSManagedObjectContext = CoreDataStack.shared.mainContext) {
self.managedObjectContext = managedObjectContext
}
//MARK: Team
func saveTeam(team: Team, completionHandler: @escaping (Result<BoxscoreTeam, CDErrors>) -> Void) {
DispatchQueue.main.async {
let entity = BoxscoreTeam(context: self.managedObjectContext)
entity.id = team.id
entity.clubName = team.clubName
entity.categorie = team.categorie?.rawValue
entity.name = team.name
entity.teamNumber = team.teamNumber
do {
try self.managedObjectContext.save()
completionHandler(.success(entity))
} catch {
print(error)
completionHandler(.failure(.saveError))
}
}
}
func fetchTeam(completionHandler: @escaping (Result<[BoxscoreTeam], CDErrors>) -> Void) {
DispatchQueue.main.async {
let request: NSFetchRequest = BoxscoreTeam.fetchRequest()
do {
let fetchedTeams = try self.managedObjectContext.fetch(request)
return completionHandler(.success(fetchedTeams))
} catch {
print("Fetch teams fails with error : \(error.localizedDescription)")
return completionHandler(.failure(.noData))
}
}
}
}
所有团队视图:
struct AllTeamsView: View {
@ObservedObject public var viewModel: TeamViewModel = TeamViewModel()
var body: some View {
VStack {
// if viewModel.fetchedTeams.isEmpty {
// Text("No team regristred, add a new team")
// } else {
List {
ForEach(viewModel.fetchedTeams, id: \.id) { item in
ZStack {
NavigationLink(destination: TeamDetailsView(viewModel: viewModel, item: item)) {
EmptyView()
}
.opacity(0.0)
TeamRowView(item: item)
}
.listRowInsets(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5))
.listRowSeparator(.hidden)
}
// .onDelete(perform: removeTeam)
}
.listStyle(InsetListStyle())
// }
Spacer()
}
.alert(viewModel.error,
isPresented: $viewModel.showTeamError,
actions: {
Button("OK", role: .cancel) { viewModel.showTeamError = false }
})
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("All teams")
.navigationBarItems(trailing:
Button {
viewModel.showNewTeamSheet = true
} label: {
Image(systemName: "plus.circle")
.tint(Color.subElement)
})
.sheet(isPresented: $viewModel.showNewTeamSheet) {
NavigationView {
NewTeamFormView(viewModel: viewModel)
}
}
}
}
新团队表单视图:
struct NewTeamFormView: View {
@StateObject public var viewModel: TeamViewModel
var body: some View {
Form {
Section {
Picker("Select a categorie", selection: $viewModel.categorie) {
ForEach(Team.Categories.allCases, id: \.self) { cat in
Text(cat.rawValue)
}
}
.pickerStyle(.menu)
Picker("Team gender", selection: $viewModel.teamGender) {
Text("Male").tag(0)
Text("Female").tag(1)
}
.pickerStyle(.segmented)
Toggle("Categorie multiple teams ?", isOn: $viewModel.isMultipleTeams)
if viewModel.isMultipleTeams {
TextField("Team number", text: $viewModel.teamNumber)
}
} header: {
Text("Team information")
}
}
.navigationTitle("New team")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: Button(action: {
viewModel.saveTeam()
}, label: {
Text("Save")
.foregroundColor(Color.subElement)
}))
}
}
团队视图模型:
public class TeamViewModel: ObservableObject {
let coreDataManager: CoreDataManager = CoreDataManager(managedObjectContext: CoreDataStack.shared.mainContext)
public var teamSamples: [Team] = [Team(categorie: .s, name: "U20-M", players: [], games: [], teamNumber: "2", score: 0, isMenTeam: true, isMultipleTeams: true)]
@Published public var categorie: Team.Categories = .u11
@Published public var teamGender: Int = 0
@Published public var teamNumber: String = ""
@Published public var isMultipleTeams: Bool = false
@Published public var playerName: String = ""
@Published public var playerNumber: String = ""
@Published public var showNewTeamSheet: Bool = false
@Published public var showNewPlayerSheet: Bool = false
@Published public var showTeamError: Bool = false
@Published public var error: String = ""
@Published public var fetchedPlayers: [Player] = []
@Published public var fetchedTeams: [Team] = []
public var teamId: UUID = UUID()
init() {
self.fetchTeams()
}
public func fetchTeams() {
self.fetchedTeams = []
DispatchQueue.main.async {
self.coreDataManager.fetchTeam { result in
switch result {
case .success(let teams):
teams.forEach { bsTeam in
let team = Team(id: bsTeam.id ?? UUID(),
clubName: bsTeam.clubName ?? "",
categorie: Team.Categories(rawValue: bsTeam.categorie ?? "") ?? .s,
name: bsTeam.name ?? "",
players: self.fetchedPlayers.filter({ $0.teamId == self.teamId }),
games: nil,
teamNumber: bsTeam.teamNumber)
self.fetchedTeams.append(team)
}
case .failure(let error):
self.showTeamError = true
self.error = error.localizedDescription
}
}
}
}
public func saveTeam() {
DispatchQueue.main.async {
let team = Team(categorie: self.categorie,
name: "\(self.categorie.rawValue) - \(self.teamGender == 0 ? "M" : "F") \(self.isMultipleTeams ? self.teamNumber : "")",
players: [],
games: [],
teamNumber: self.teamNumber,
score: 0,
isMenTeam: self.teamGender == 0,
isMultipleTeams: self.isMultipleTeams)
self.coreDataManager.saveTeam(team: team, completionHandler: { result in
switch result {
case .success:
DispatchQueue.main.async {
self.fetchTeams()
self.showNewTeamSheet = false
}
case .failure(let error):
self.showTeamError = true
self.error = error.localizedDescription
}
})
}
}
}
我已经用了两天了,如果有人发现异常,我洗耳恭听。
编辑:问题出在CoreDataStack中,请参见下面的新实现
class CoreDataStack {
// MARK: - Singleton
static let shared: CoreDataStack = CoreDataStack(modelName: "Boxscore")
var persistentContainer: NSPersistentContainer
// MARK: - Variables
var mainContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
// MARK: - Init
init(modelName: String, persistentStoreDescription: String? = nil) {
persistentContainer = NSPersistentContainer(name: modelName)
if let psd = persistentStoreDescription {
let description = NSPersistentStoreDescription()
description.url = URL(fileURLWithPath: psd)
persistentContainer.persistentStoreDescriptions = [description]
}
persistentContainer.loadPersistentStores(completionHandler: { (_, error) in
guard let unwrappedError = error else { return }
fatalError("Unresolved error \(unwrappedError.localizedDescription)")
})
}
}
1条答案
按热度按时间yjghlzjz1#
/dev/null
作为核心数据栈的文件URL创建了一个内存中的SQLite存储,这对于核心数据模型的单元测试和SwiftUI预览非常好,但对于实际的持久性来说就不那么好了。使用该路径不应该是默认的,它应该是上面提到的用例的一个选择。