I am implementing Swift CoreData into my application, However I continue to face the error 'Unrecognised selector sent to Instance' when adding data to the class set up as you can see below. I realise that this is probably a rookie error, so thanks for your patience.
func coreDataRequest(){
container = NSPersistentContainer(name: "tileData")
container.loadPersistentStores { StoreDescription, error in
if let error = error {
print("Unresolved Error \(error)")
}
}
var campaign = Campaign()
campaign.countryName = "France"
}
The Error
unrecognized selector sent to instance
Here is my XCDataModel
Have you generate Campaign() class, that you add to tileData.xcdatamodeld?
if so create a NSManagedObjectContext like this:
var context: NSManagedObjectContext = {
return persistentContainer.viewContext
}()
and then use it to create Campaign like this:
var campaign = Campaign(context: context)
campaign.countryName = "France"
then if you want to store your object you have to call save on the context:
func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch {
context.rollback()
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
I recommend you to look through few tutorials, it would save your time in future. There are a lot of them, just google it.
Without seeing all your code its difficult to say what is wrong, just from what I can see the container is missing a "let" in-front unless that was declared outside the function. This is a really good tutorial on setting up and using CoreData https://www.raywenderlich.com/books/core-data-by-tutorials/v7.0/chapters/3-the-core-data-stack#toc-chapter-006-anchor-011
Related
I am trying to get more than one entity for my coding project at school but I have an error saying invalid redeclaration of data controller.
class DataController: ObservableObject{
let container = NSPersistentContainer(name: "Blood Sugar")
init() {
container.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
}
}
class DataController : ObservableObject{
let containers = NSPersistentContainer(name: "Carbohydrates")
init(){
containers.loadPersistentStores{ description, errors in
if let errors = errors{
print("Core data failed to load: \(errors.localizedDescription)")
}
}
}
}
Since you tagged this as SwiftUI, DataController should be a struct. We use value types like structs now to solve a lot of the bugs caused by using objects in UIKit and ObjC. You can see Apple's doc Choosing Between Structures and Classes for more info.
If you use an Xcode app template project and check "Use core data" you'll see a PersistenceController struct that will demonstrate how to do it correctly. I've included it below:
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.timestamp = 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: "SearchTest")
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)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
You can create more entities in the model editor. There is usually only one NSPersistentContainer per app. The container can have multiple stores (usually sqlite databases). Then you can assign different entities to each store too. To create an instance of an entity you do that on a NSManagedObjectContext and you can choose which store to save it too, although most of the time people use one store which is the default.
I'm trying to use CoreData to save my favourites locally and I have a set that acts as storage so that they will be accessed when the program terminates but I am not sure how to make my object persistent to do that. This is what I tried to do.
import Foundation
import CoreData
class PersistenceService {
private init() {}
static var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
// MARK: CoreData
static var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "playerModel")
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)")
}
})
return container
}()
static func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.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)")
}
}
}
}
This is the object I want to save
var item = CurrentPlayers(context: PersistenceService.context) //make it reference NSManagededContext so it can be saved
//this method adds to Favourites
#IBAction func addToFav(_ sender: Any) {
let alert = UIAlertController(title: "Favourite Added 💙", message: "\(name.text ?? "") is added to favourites", preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertAction.Style.default)
{ [self] _ in
FavouriteManager.shared.add(item)
})
self.present(alert, animated: true, completion: nil)
print("Favourite button Pressed")
}
This is where I want to store my objects
import Foundation
class FavouriteManager {
static let shared = FavouriteManager()
var favSet: Set<CurrentPlayers> = Set()
func add(_ player: CurrentPlayers) {
favSet.insert(player)
NotificationCenter.default.post(
name: .passFavNotification,
object: player
)
}
}
I'm trying to save data in the local storage using Core Data and I'm getting this error :
No NSEntityDescriptions in any model claim the NSManagedObject
subclass 'TrackItem' so +entity is confused. Have you loaded your
NSManagedObjectModel yet ?
#IBAction func AddTrack(_ sender: Any) {
print("I made it to AddTrack§§§§§")
let Trackitem = TrackItem(context: PersistenceService.context)
Trackitem.kms = Int32(kmsField!.text!)!
Trackitem.liters = Float(litersField!.text!)!
Trackitem.date = textFieldPicker!.text!
PersistenceService.saveContext()
}
This is the function of the button to save. Knowing that I made a CoreData model with the entity and its attributes and CreateNSObject... and I have the class and the extension of the Model. But After adding some content in the input fields and try to save the app stops working and gives me this error.
import Foundation
import CoreData
class PersistenceService {
private init() {
}
static var context:NSManagedObjectContext{
return persistentContainer.viewContext
}
// MARK: - Core Data stack
static var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "FillMyTank")
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)")
}
})
return container
}()
// MARK: - Core Data Saving support
static func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.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)")
}
}
}
}
Had the same problem. For me it was an annotation that I deleted before the class definition, which looks like #objc(CLASSNAME). So for example:
#objc(Person)
public class Person
I have my entity codegen on Manual/None, then while in the datamodel I click on: Editor -> Create NSManagedObject Subclass.
I put the core data stack in its own file as shown below. I read that using dependency injection is the best way to pass the managed object context. So in each of a handful of VCs, I declare the following property:
var managedObjectContext: NSManagedObjectContext?
Now, the tricky part is getting the moc from my stack to the different VCs. Which seems like a great place for a singleton, but assuming that's a bad idea, I guess I would use the code below in CoreDataStack:
let controller = self.window!.rootViewController as! ViewController
let context = self.persistentContainer.viewContext
controller.managedObjectContext = context
But that leaves me with a few questions:
1) Where in CoreDataStack should I include the code above? In the App Delegate it would go in didFinishLaunchingWithOptions, but that's not really an option now.
2) Writing the above code for every single vc that needs a context seems bad. I guess I could loop through all the VCs. I've seen the moc passed using didSet too, but that doesn't seem quite right either.
CoreData Stack
class CoreDataStack {
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
I have just downloaded the new Xcode 7.0 beta and did a migration from Swift 1.2 to Swift 2. The migration apparently did not change the whole code, in fact a method saveContext() which was fine until throws 2 errors for the line:
if moc.hasChanges && !moc.save() {
Binary operator '&&' cannot be applied to two Bool operands
and
Call can throw, but it is not marked with 'try' and the error is not handled
The method looks like this:
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save() {
// Replace this implementation with code to handle the error appropriately.
// abort() 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.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
Any ideas on how to get it working?
The first of the two errors you provided is misleading, but the second is spot on. The problem is in !moc.save() which as of Swift 2, no longer returns Bool and is instead annotated throws. This means that you you have to try this method and catch any exceptions that it may emit, instead of just checking wether its return value is true or false.
To reflect this, a new project created in Xcode 7 using Core Data will produce the following boilerplate code which can replace the code you're using.
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() 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
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
The answer by 0x7fffffff is correct, but to improve upon Apple's boilerplate code, you can catch the specific error in the catch block using catch let error as NSError like so:
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch let error as NSError {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
The best practice is to use the var error witch will still be available if you just use it this way :
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
In the same way, if you are sure that managedObjectContext.save() will not throw an exception, the code is slimmed down to:
func saveContext () {
if managedObjectContext.hasChanges {
try! managedObjectContext.save()
}
}
And to extrapolate on why managedObjectContext is not optional in the Swift 2 code, it is because the NSManagedObject(concurrencyType:) is an initializer that does not fail. In Xcode 6, the boilerplate code returned an optional context if the NSPersistentStoreCoordinator is nil, but you can handle this easily by checking.
lazy var managedObjectContext: NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
var moc = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()