I have an issue with fetching a Core Data managed object which has an enum
attribute with Int16
rawValues. The program crashes when calling NSPredicate(format:)
.
This is the code (3rd line) that results a crash:
func returnBook (bookType: BookType) -> Book? {
let fetchRequest = NSFetchRequest<Book>(entityName: "Book")
fetchRequest.predicate = NSPredicate(format: "%K = %@", #keyPath(Book.bookType), bookType.rawValue as CVarArg)
fetchRequest.fetchLimit = 1
let book = (try? fetchRequest.execute())?.first
return book
}
Crash message is:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x3)
When I change the 3rd line with the following, I get another kind of error.
This is the alternative code:
fetchRequest.predicate = NSPredicate(format: "%K = %@", #keyPath(Book.bookType), bookType as! CVarArg)
And this is the error resulted by that alternative code:
Thread 1: signal SIGABRT
And I see this in the debug output:
Could not cast value of type 'ProjectName.BookType' (0x10ac10098) to 'Swift.CVarArg' (0x10b698548).
Here are some other related code in the Project:
@objc(Book)
public class Book: NSManagedObject {
// …
}
extension Book {
@NSManaged public var bookType: BookType
}
@objc public enum BookType: Int16 {
case fiction = 0
case nonFiction = 1
case reference = 2
}
My question: How can I properly fetch the Book
object from my Core Data store by using its bookType
attribute, which is in an @objc enum
type with Int16
rawValues?
Update:
I have tried something else as suggested in a presentation by Jesse Squires:
- Removed
@objc
keyword fromenum
's definition - Used
private
andpublic
properties to access theenum
's instances...
I have changed my code as suggested in the linked presentation, and I have changed the name of bookType
attribute in Core Data's ".xcdatamodeld" file to bookTypeValue
.
extension Book {
public var bookType: BookType {
get {
return BookType(rawValue: self.bookTypeValue)!
}
set {
self.bookTypeValue = newValue.rawValue
}
}
@NSManaged private var bookTypeValue: Int16
}
public enum BookType: Int16 {
case fiction = 0
case nonFiction = 1
case reference = 2
}
And I tried to fetch using the private value as suggested in the presentation linked above...:
fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %@", bookType)
However I could not solve my problem with this method either... XCode complains immediately:
Argument type 'BookType' does not conform to expected type 'CVarArg'
XCode stops complaining if I use bookType.rawValue
instead. However, program crashes during runtime with the same error above (first one).
Update 2:
I have applied the answers given to this question which is suggested by the community. To be more specific, I have tried the suggestion in the answer to that question which is using %i
, or @ld
instead of %@
like so:
fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %i", bookType.rawValue)
and I have tried the other suggestion which is using String interpolation like so:
fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %@", "\(bookType.rawValue)")
These code changes prevented crashes, but func returnBook
returns nil, even if I am sure that the data is there. Fetch or comparison fails for some reason...