#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, чтобы ее можно было идентифицировать.