Subclass.Fetchrequest() Swift 3.0, Extension Not Really Helping 100%

invalid redeclaration in auto code generate NSManagedObject Subclass Swift 3

In Xcode 8.1, before using the auto code generator, you must select the entity in your data model:

Entity

Then go to the Data Model Inspector tab:

Data Model Inspector

Under "Codegen" select "Manual/Node"

After that you could create a NSManagedObject subclass without errors.


Alternatively, if you have already used 'Class Definition', you can go into your existing .xcdatamodeld file and set all current entities to 'Manual/None' under Codegen. Make sure to save your project (File -> Save), delete your existing Derived Data, clean the project, and then build. Resolved it for me without having to re-make my whole model.

SwiftUI FetchRequest Error: 'A fetch request must have an entity'

Try the code from the SwiftUI app project template when Core Data support is checked:

MyApp.swift

import SwiftUI

@main
struct MyApp: App {
let persistenceController = PersistenceController.shared

var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}

Persistance.swift

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: "MyApp")
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)")
}
})
}
}

Convert request Function to Generic type

According to my comment I recommend to use a protocol with extension for example

protocol Fetchable
{
associatedtype FetchableType: NSManagedObject = Self

static var entityName : String { get }
static var managedObjectContext : NSManagedObjectContext { get }
static func objects(for predicate: NSPredicate?) throws -> [FetchableType]
}

extension Fetchable where Self : NSManagedObject, FetchableType == Self
{
static var entityName : String {
return NSStringFromClass(self).components(separatedBy: ".").last!
}

static var managedObjectContext : NSManagedObjectContext {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}

static func objects(for predicate: NSPredicate?) throws -> [FetchableType]
{
let request = NSFetchRequest<FetchableType>(entityName: entityName)
request.predicate = predicate
return try managedObjectContext.fetch(request)
}
}

Change (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext to the reference to your managed object context.

Make all NSManagedObject subclasses adopt Fetchable. There is no extra code needed in the subclasses.

Now you can get the data with

do {
let predicate = NSPredicate(format: ...
let objects = try MyEntity.objects(for: predicate)
} catch {
print(error)
}

That's all, objects are [MyEntity] without any type casting and always non-optional on success.

The protocol is easily extendable by default sorting descriptors, sorting directions etc.

Default implementation of protocol extension in Swift not working

We'll use a more scaled-down example (below) to shed light on what goes wrong here. The key "error", however, is that Case cannot make use of the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject; since type Int does not conform to the type constraint AnyObject. The latter is used to represent instances of class types, whereas Int is a value type.

AnyObject can represent an instance of any class type.

Any can represent an instance of any type at all, including function types.

From the Language Guide - Type casting.

Subsequently, Case does not have access to any implementation of the blueprinted objectWithId() method, and does hence not conform to protocol ObjectByIdFetchable.


Default extension of Foo to T:s conforming to Any works, since Int conforms to Any:

protocol Foo {
typealias T
static func bar()
static func baz()
}

extension Foo where T: Any {
static func bar() { print ("bar") }
}

class Case : Foo {
typealias T = Int

class func baz() {
print("baz")
}
}

The same is, however, not true for extending Foo to T:s conforming to AnyObject, as Int does not conform to the class-type general AnyObject:

protocol Foo {
typealias T
static func bar()
static func baz()
}

/* This will not be usable by Case below */
extension Foo where T: AnyObject {
static func bar() { print ("bar") }
}

/* Hence, Case does not conform to Foo, as it contains no
implementation for the blueprinted method bar() */
class Case : Foo {
typealias T = Int

class func baz() {
print("baz")
}
}

Edit addition: note that if you change (as you've posted in you own answer)

typealias T = Int

into

typealias T = NSNumber

then naturally Case has access to the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject, as NSNumber is class type, which conforms to AnyObject.


Finally, note from the examples above that the keyword override is not needed for implementing methods blueprinted in a protocol (e.g., entityName() method in your example above). The extension of Case is an protocol extension (conforming to ObjectByIdFetchable by implementing blueprinted types and methods), and not really comparable to subclassing Case by a superclass (in which case you might want to override superclass methods).

SwiftUI @FetchRequest crashes the app and returns error

In the @enviromentVAr, sometimes, you need to set the enviroment by yourself.

 let managedObjectContext: NSManagedObjectContext =  ((UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext)!

ContentView().environment(\.managedObjectContext, managedObjectContext)

Then the managedObjectContext can work.

 @Environment(\.managedObjectContext) var managedObjectContext

Why is executeFetchRequest not returning subclass objects when run under XCTest using Swift?

I had a similar question. Making my entity class public solves the issue and I get the right class type.(CoreData class miss match in unit test)

Xcode 8 generates broken NSManagedObject subclasses for iOS 10

I finally got mine to work. Here is what I did. (Flights is one of my entities)

I setup the xcdatamodeld as follows

Sample Image

And then the entity as

Sample Image

Then I used Editor -> Create NSManagedObject Subclass

This creates two files for my flights entity

Flights+CoreDataProperties.swift

Flights+CoreDataClass.swift

I renamed Flights+CoreDataClass.swift to Flights.swift

Flights.swift is just

import Foundation
import CoreData

@objc(Flights)
public class Flights: NSManagedObject {

}

Flights+CoreDataProperties.swift is

import Foundation
import CoreData


extension Flights {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Flights> {
return NSFetchRequest<Flights>(entityName: "Flights");
}

@NSManaged public var ...
}

This appears to work for me.I could not get Codegen to work in any other way, even though I tried many of the suggestions that were out there.

Also this had me scratching my head for a while and I add it as an assist. Don't forget with the new Generics version of the FetchRequest you can do this

let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Flights")

Difficulty figuring out how to fetch an NSManagedObject subclass entity from CoreData

Just to give you a code sample to compere to, without exactly explaining all the places that are wrong, and obviously i can't test this so some things may still be problematic...:

let targetValue = "CrawlerDistance"
let request = NSFetchRequest<CrawlerOne>(entityName: "CrawlerOne")
request.predicate = Predicate(format: "crawlerDistance = %@" , targetValue)

let appDel:AppDelegate = (UIApplication.shared().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext

var results:NSArray = try context.executeFetchRequest(request)

What is the reason for having function for fetchRequest on an NSManagedObject class

It is a convenience factory method to be able to create a fetch request type safe with

let request : NSFetchRequest<Sensor> = Sensor.fetchRequest()

along the lines of the convenience initializer syntax

let sensor = Sensor(context: context)

It's not required.



Related Topics



Leave a reply



Submit