ios CoreData:重新打开应用程序后无法检索保存的数据

vuktfyat  于 2023-02-01  发布在  iOS
关注(0)|答案(1)|浏览(170)

在我的应用程序中,我尝试保存团队对象。
为了保存这个对象,我创建了一个带有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)")
            })
            
        }
    }
yjghlzjz

yjghlzjz1#

/dev/null作为核心数据栈的文件URL创建了一个内存中的SQLite存储,这对于核心数据模型的单元测试和SwiftUI预览非常好,但对于实际的持久性来说就不那么好了。使用该路径不应该是默认的,它应该是上面提到的用例的一个选择。

相关问题