Ошибка CoreData «сохранить ()» при использовании CoreData с SwiftUI

#ios #swift #core-data #swiftui

Вопрос:

Я создаю приложение, которое использует основные данные для сохранения пользовательских данных, и всякий раз, когда я сохраняю новые значения данных, оно выдает ошибку ниже. вот скриншот моего .xcdatamodel
Скриншот моей структуры данныхвот моего persistance.swift , который поставляется с шаблоном SwiftUI CoreData

 
//
//  Persistence.swift
//  Permit Tracker
//
//  Created by Everett Wilber on 8/9/21.
//

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
//        for _ in 0..<10 {
//            let newItem = Item(context: viewContext)
//          newItem.coordinate = Date()
//        }
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error (nsError), (nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "Permit_Tracker")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                Typical reasons for an error here include:
                * The parent directory does not exist, cannot be created, or disallows writing.
                * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                * The device is out of space.
                * The store could not be migrated to the current model version.
                Check the error message to determine what the actual problem was.
                */
                fatalError("Unresolved error (error), (error.userInfo)")
            }
        })
    }
}
 

вот мой ContentView.swift

 //
//  ContentView.swift
//  Permit Tracker
//
//  Created by Everett Wilber on 8/9/21.
//

import SwiftUI
import CoreData
import CoreLocation
import BackgroundTasks

struct ContentView: View {
    @StateObject var locationViewModel = LocationViewModel()
    @Environment(.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: Item.date, ascending: true)],
        animation: .default
    )
    private var items: FetchedResults<Item>

    @State var Recording = false
    @State var AllDrives: [DriveDetails] = []
    
    let locationManager = CLLocationManager()
    
    var body: some View {
        Group {
            VStack {
                if (Recording) {
                    TrackingView(locationViewModel: locationViewModel)
                        .onAppear(perform: {
                            print("appeared recording")
                        })
                } else {
                    List {
                        ForEach(0..<items.count, content: {i in
                            Drive(locationViewModel: locationViewModel, driveDetail: items[i].location?.driveDetail)
                        })
                    }
                    .onAppear(perform: {
                        print("appeared list", items.first?.date as Any)
                    })
                }
                HStack {
                    ToolBar(Recording: $Recording, StartRecording: startRecording, StopRecording: stopRecording)
                }
            }
        }
        .onAppear(perform: {
            locationViewModel.requestPermission()
        })
    }
    private func startRecording() {
        if CLLocationManager.locationServicesEnabled() {
            locationViewModel.locationManager.activityType = .automotiveNavigation
            locationViewModel.locationManager.startUpdatingLocation()
        } else {
            print("location denied")
        }
    }
    
    private func stopRecording() {
        if CLLocationManager.locationServicesEnabled() {
            
            
            locationViewModel.locationManager.stopUpdatingLocation()
            let allLocations = locationViewModel.allLocations
            let newDrive = locationDelist(locations: allLocations, date: allLocations.first?.timestamp ?? Date())
            
            let newItem = Item(context: viewContext)
            newItem.location = newDrive
            newItem.date = (allLocations.first?.timestamp ?? Date())
            
            print(
                "hasChanges:", viewContext.hasChanges,
                "ninsertedObjects:", viewContext.insertedObjects
            )
            viewContext.userInfo.setValue("data", forKey: "use")
            
            do {
                try viewContext.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//              let nsError = error as NSError
                print("error", error)
//              fatalError("Unresolved error (nsError), (nsError.userInfo)")
            }
            locationViewModel.allLocations = []
        } else {
            print("location denied")
        }
    }
//
//    private func deleteItems(offsets: IndexSet) {
//        withAnimation {
//            offsets.map { items[$0] }.forEach(viewContext.delete)
//
//            do {
//                try viewContext.save()
//            } catch {
//                // Replace this implementation with code to handle the error appropriately.
//                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//                let nsError = error as NSError
//                fatalError("Unresolved error (nsError), (nsError.userInfo)")
//            }
//        }
//    }
    
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}
 

here is a screen recording of it running, the error happens when i stop recording location on line 88 of ContentView.swift

YouTube video of a simulator, simulating the app

once the app is quit, the data is lost.

when i saw 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release in the log i looked through my code for NSKeyedUnarchiveFromData . As it turns out, i am using NSSecureUnarchiveFromDataTransformer (keyword secure) the issue with NSKeyedUnarchiveFromData is it is insecure.

I use a transformable data type because it stores a list of locations. this combined creates a list of lists of CLLocations (stored split up). here is the DriveDetails.swift file, where i have the classes that store DriveDetails which needs to be saved and convert it into CoreData. All of this is in my git

 //
//  DriveDetails.swift
//  Permit Tracker
//
//  Created by Everett Wilber on 8/9/21.
//

import Foundation
import CoreLocation

class DriveDetails {
    init(Locations: [CLLocation]) {
        self.Locations = Locations
    }
    init(Count: Int, coordinatesLon: [Double], coordinatesLat: [Double] , altitudes: [Double], horizontalAccuracies: [Double], verticalAccuracies: [Double], courses: [Double], courseAccuracies: [Double], speeds: [Double], speedAccuracies: [Double], timestamps: [Date]) {
        for i in 0..<Count {
            let location: CLLocation = CLLocation(
                coordinate: CLLocationCoordinate2D(latitude: coordinatesLat[i], longitude: coordinatesLon[i]),
                altitude: altitudes[i],
                horizontalAccuracy: horizontalAccuracies[i],
                verticalAccuracy: verticalAccuracies[i],
                course: courses[i],
                courseAccuracy: courseAccuracies[i],
                speed: speeds[i],
                speedAccuracy: speedAccuracies[i],
                timestamp: timestamps[i]
            )
            self.Locations.append(location)
        }
    }
    var Date: Date {
        get {
            return Locations.first?.timestamp ?? Foundation.Date()
        }
    }
    var Locations: [CLLocation] = []
    var Locations2d: [CLLocationCoordinate2D] {
        get {
            var locs: [CLLocationCoordinate2D] = []
            for i in Locations {
                locs.append(i.coordinate)
            }
            return locs
        }
    }
}

@objc
public class locationDelist: NSObject {
    var coordinatesLon: [Double] = []
    var coordinatesLat: [Double] = []
    var altitudes: [Double] = []
    var horizontalAccuracies: [Double] = []
    var verticalAccuracies: [Double] = []
    var courses: [Double] = []
    var courseAccuracies: [Double] = []
    var speeds: [Double] = []
    var speedAccuracies: [Double] = []
    var timestamps: [Date] = []
    var date: Date
    
    init(locations: [CLLocation], date: Date) {
        self.date = date
        for location in locations {
            
            self.coordinatesLat.append(location.coordinate.latitude)
            self.coordinatesLon.append(location.coordinate.longitude)
            self.altitudes.append(location.altitude)
            
            self.horizontalAccuracies.append(location.horizontalAccuracy)
            self.verticalAccuracies.append(location.verticalAccuracy)
            
            self.courses.append(location.course)
            self.courseAccuracies.append(location.courseAccuracy)
            
            self.speeds.append(location.speed)
            self.speedAccuracies.append(location.speedAccuracy)
            
            self.timestamps.append(location.timestamp)
            
        }
    }
    var driveDetail: DriveDetails {
        get {
            DriveDetails(
                Count: timestamps.count,
                coordinatesLon: self.coordinatesLon,
                coordinatesLat: self.coordinatesLat,
                altitudes: self.altitudes,
                horizontalAccuracies: self.horizontalAccuracies,
                verticalAccuracies: self.verticalAccuracies,
                courses: self.courses,
                courseAccuracies: self.courseAccuracies,
                speeds: self.speeds,
                speedAccuracies: self.speedAccuracies,
                timestamps: self.timestamps
            )
        }
    }
}

class LocationToDataTransformer: NSSecureUnarchiveFromDataTransformer {
    
    override class func allowsReverseTransformation() -> Bool {
        return true
    }
    
    override class func transformedValueClass() -> AnyClass {
        return locationDelist.self
    }
    
    override class var allowedTopLevelClasses: [AnyClass] {
        return [locationDelist.self]
    }

    override func transformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else {
            fatalError("Wrong data type: value must be a Data object; received (type(of: value))")
        }
        return super.transformedValue(data)
    }
}

extension NSValueTransformerName {
    static let locationToDataTransformer = NSValueTransformerName(rawValue: "LocationToDataTransformer")
}
 

вот мерзавец в сломанном коммите, если это поможет

наконец, вот журналы, которые печатаются непосредственно перед возникновением ошибки:

 2021-08-12 13:52:14.718185-0400 Permit Tracker[66730:1242980] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-12 13:52:14.718692-0400 Permit Tracker[66730:1242980] -[Permit_Tracker.locationDelist encodeWithCoder:]: unrecognized selector sent to instance 0x6000031fb2a0
2021-08-12 13:52:14.719395-0400 Permit Tracker[66730:1242980] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-12 13:52:14.719792-0400 Permit Tracker[66730:1242980] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-12 13:52:14.720159-0400 Permit Tracker[66730:1242980] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-12 13:52:14.732536-0400 Permit Tracker[66730:1242980] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-12 13:52:14.732890-0400 Permit Tracker[66730:1242980] -[Permit_Tracker.locationDelist encodeWithCoder:]: unrecognized selector sent to instance 0x6000031fb2a0
2021-08-12 13:52:14.733299-0400 Permit Tracker[66730:1242980] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-12 13:52:14.733533-0400 Permit Tracker[66730:1242980] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020c6f40> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020c6f40> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-12 13:52:14.733824-0400 Permit Tracker[66730:1242980] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-12 13:52:14.734242-0400 Permit Tracker[66730:1242980] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-12 13:52:14.734546-0400 Permit Tracker[66730:1242980] -[Permit_Tracker.locationDelist encodeWithCoder:]: unrecognized selector sent to instance 0x6000031fb2a0
2021-08-12 13:52:14.734930-0400 Permit Tracker[66730:1242980] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-12 13:52:14.735153-0400 Permit Tracker[66730:1242980] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-12 13:52:14.735339-0400 Permit Tracker[66730:1242980] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-12 13:52:14.746477-0400 Permit Tracker[66730:1242980] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-12 13:52:14.746945-0400 Permit Tracker[66730:1242980] -[Permit_Tracker.locationDelist encodeWithCoder:]: unrecognized selector sent to instance 0x6000031fb2a0
2021-08-12 13:52:14.747505-0400 Permit Tracker[66730:1242980] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-12 13:52:14.747902-0400 Permit Tracker[66730:1242980] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000020d18c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
 

вот в чем ошибка

 Error Domain=NSCocoaErrorDomain Code=134060 "A Core Data error occurred."
 

Я пробовал возиться с xcdatafile этой проблемой весь день.

Комментарии:

1. Здесь очень много кода, который не имеет отношения к делу, но я предполагаю, что проблема связана с преобразованием местоположения.

2. @JoakimDanielson это то, что я предполагал, но я не могу понять, в какой части я ошибся. Ошибка невероятно расплывчата, и у apple нет никакой документации по этой ошибке. «произошла ошибка основных данных» вау, полезно

3. Ну, и мы тоже не можем, так как мы не знаем, как происходит трансформация. Но действительно ли вам нужно проходить через трудности с использованием Transformable, когда вы можете легко сохранить местоположение в виде двух двойных свойств, долготы и широты?

4. Как я уже сказал, создайте новую сущность с необходимыми атрибутами, а затем создайте связь между ними. Если вы не уверены, как это сделать, прочтите документацию или найдите учебник

5. @JoakimDanielson прав. Вместо того, чтобы иметь свой locationDelist класс, это должна быть сущность в отношениях. Хорошее эмпирическое правило с CoreData состоит в том, чтобы упростить его, если сможете. Просто поместите свойства в Сущность в качестве атрибутов. Кроме того, вы сохранили их в виде массивов внутри класса, но, похоже, они должны храниться как массив класса. Я бы рекомендовал отношения «Один ко многим» между вашими сущностями. Кроме того, ваша сущность товара должна иметь свойство ID, чтобы ее можно было идентифицировать.