I've recently ran into an issue with core data that I'm unable to solve.
I have the following object and relationships Recommendation <---> Item
extension Recommendation {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Recommendation> {
return NSFetchRequest<Recommendation>(entityName: "Recommendation")
}
#NSManaged public var age: Int?
#NSManaged public var tags: Array<String>?
#NSManaged public var object: Item?
}
extension Item {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
return NSFetchRequest<Recommendation>(entityName: "Item")
}
#NSManaged public var id: String?
#NSManaged public var name: String?
#NSManaged public var recommendation: Recommendation?
}
I want to fetch all Items based on attributes in their related Recommendation.
Some of these attributes could be missing.
How would I create a NSPredicate to fetch objects where the Recommendation.age or Recommendation.tags could be non-existent (applicable to all ages/tags) or should be compared against a provided value?
Should I always provide a Default Value?
I Have a many to many relationship from WishList to Product. I made codegen property as Manual/None, so I have created respected classes as below.
class Product: NSManagedObject {
struct constants {
static let ID = "id"
static let NAME = "name"
static let DESCRIPTION = "description"
static let wishLists = "shoppingLists"
}
}
extension Product {
#NSManaged public var id: Int64
#NSManaged public var name: String
#NSManaged public var desc: String
#NSManaged public var wishLists: NSOrderedSet?
}
class WishList: NSManagedObject {
struct constants {
static let ID = "id"
static let NAME = "name"
static let PRODUCT = "products"
}
func addProducts(_values : NSMutableOrderedSet) {
}
func removeProducts(_values : NSMutableOrderedSet) {
}
func addProduct(_values : Product) {
}
func removeProduct(_values : Product) {
}
}
extension WishList {
#NSManaged public var id: Int64
#NSManaged public var name: String
#NSManaged public var products: NSOrderedSet?
}
What I need is, How do I implement convenience methods to achieve below functionalities, since core data does not create on behalf of me because I made codegen as Manual/None.
User can add a product into multiple wish lists
Each wishList can have multiple products.
With Codegen set to Manual/None, you're free to write your own implementation or let Xcode generate an appropriate subclass for you. (Editor -> Create NSManagedObject Subclass...) All the helper methods would be readily available there. (assuming NSOrderedSet)
extension WishList {
#objc(insertObject:inProductsAtIndex:)
#NSManaged public func insertIntoProducts(_ value: Product, at idx: Int)
#objc(removeObjectFromProductsAtIndex:)
#NSManaged public func removeFromProducts(at idx: Int)
#objc(insertProducts:atIndexes:)
#NSManaged public func insertIntoProduct(_ values: [Product], at indexes: NSIndexSet)
#objc(removeProductsAtIndexes:)
#NSManaged public func removeFromProducts(at indexes: NSIndexSet)
#objc(replaceObjectInProductsAtIndex:withObject:)
#NSManaged public func replaceProducts(at idx: Int, with value: Product)
#objc(replaceProductsAtIndexes:withProducts:)
#NSManaged public func replaceProducts(at indexes: NSIndexSet, with values: [Product])
#objc(addProductsObject:)
#NSManaged public func addToProducts(_ value: Product)
#objc(removeProductsObject:)
#NSManaged public func removeFromProducts(_ value: Product)
#objc(addProducts:)
#NSManaged public func addToProducts(_ values: NSOrderedSet)
#objc(removeProducts:)
#NSManaged public func removeFromProducts(_ values: NSOrderedSet)
}
...and vice-versa for Product and its wishlists.
Is this good or bad code for MVVM pattern with Core Data. For example i have Category+CoreDataClass.swift and CategoryDataProperties.swift; thats my
public class Account: NSManagedObject {
//what should i write here; any clue
}
extension Account {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Category> {
return NSFetchRequest< Category >(entityName: "Category")
}
#NSManaged public var categoryId: Int64
#NSManaged public var categoryTitle: String
}
Model = Category
class CategoryViewModel{
var category:Category() //
var categories: [Category]()
func AddCategory(category) {
//pass category to CoreData save method in CoreDataService.swift
}
}
and ViewModel is used in Single Screen with two textField named categoryId,categoryName(UIView)
class CategoryVC: UITableViewController {
var vm: CategoryViewModel!
}
hI: I've been using EVReflection to make our Network Layer Fully Restful and I Must say: AWESOME WORK! Thanks to #evermeer for this Library. you can get it here: https://github.com/evermeer/EVReflection
Now, to the Issue:
The Next Step is to get those Objects Straight into CORE DATA. Here is one of the Classes in Question
// Here is the Object With the EVReflectable Extension as the Documentation Claims:
import Foundation
import CoreData
import EVReflection
public class NGTripSummary: NSManagedObject { }
extension NGTripSummary: EVReflectable { }
// and HERE are the Object Properties:
// NGTripSummary+CoreDataProperties.swift
import Foundation
import CoreData
extension NGTripSummary {
#nonobjc public class func fetchRequest() -> NSFetchRequest<NGTripSummary> {
return NSFetchRequest<NGTripSummary>(entityName: "NGTripSummary")
}
#NSManaged public var carId: Int64
#NSManaged public var citiesVisited: NSObject?
#NSManaged public var cost: Double
#NSManaged public var distance: Double
#NSManaged public var globalStartDate: NSDate?
#NSManaged public var globalEndDate: NSDate?
#NSManaged public var kpl: Double
#NSManaged public var litres: Double
#NSManaged public var routeLocations: NSObject?
#NSManaged public var sessionId: Int64
#NSManaged public var localStartDate: NSDate?
#NSManaged public var localEndDate: NSDate?
#NSManaged public var duration: Int64
#NSManaged public var speed: Double
#NSManaged public var _id: Int64
#NSManaged public var sessionUuid: String?
#NSManaged public var tripUuid: String?
}
// . here is the JSON String that Represents a Demo Object:
let tripData = "{\"id\":26105240,\"userId\":25796277,\"carId\":25817551,\"vehicleId\":57812351,\"sessionUuid\":\"53324259-aa69-41c8-8f9e-c62bdb70f165\",\"tripUuid\":\"afdd8f55-6d14-4cf9-bd9f-5b6da47aaf93\",\"localStartDate\":1487170622490,\"localEndDate\":1487178323654,\"globalStartDate\":1487163422490,\"globalEndDate\":1487171123654,\"routeLocations\":null,\"litres\":24.7699,\"kpl\":0.0772,\"cost\":153.3258,\"distance\":1.9132,\"duration\":491.958,\"speed\":14.0}"
// and HERE is the Method I'm Trying to use to Create this Mock Object:
func makeMockData() {
let singleTrip = NGTripSummary(json: tripData)
print("Single Trip: \(singleTrip)")
}
// NOW: When Creating the Object, the Crash Happens HERE # class EVReflection:
// Call your own object validators that comply to the format: validate<Key>:Error:
do {
var setValue: AnyObject? = value as AnyObject?
/* LINE 923: CRASH HAPPENS HERE -> */ try anyObject.validateValue(&setValue, forKey: key)
anyObject.setValue(setValue, forKey: key)
} catch _ {
(anyObject as? EVReflectable)?.addStatusMessage(.InvalidValue, message: "Not a valid value for object `\(NSStringFromClass(type(of: (anyObject as AnyObject))))`, type `\(type)`, key `\(key)`, value `\(value)`")
print("INFO: Not a valid value for object `\(NSStringFromClass(type(of: (anyObject as AnyObject))))`, type `\(type)`, key `\(key)`, value `\(value)`")
}
////////////////// REASON FOR CRASH ////////////////////
[error] error: CoreData: error: Failed to call designated initializer on NSManagedObject class 'NGTripSummary'
CoreData: error: CoreData: error: Failed to call designated initializer on NSManagedObject class 'NGTripSummary'
//////////////////////////////////////////////////
-> Anybody, PLEASE HELP :-0
The error message describes the exact problem. You appear to be creating instances of NGTripSummary with this line of code:
let singleTrip = NGTripSummary(json: tripData)
But NGTripSummary is a subclass of NSManagedObject, and you're never calling the designated initializer for NSManagedObject. That's required. You must call init(entity:insertInto:) on NSManagedObject, or else use the factory method insertNewObject(forEntityName:into:) on NSEntityDescription to get a valid managed object. If you don't, you get this specific error and your app crashes.
If you need to create instances using tripData, you can do that, but you also need to provide a managed object context and an entity description. You could do that with a convenience initializer on your class, which would call the designated initializer as part of the initialization process.
EVReflection now has a Cocoapods subspec for CoreData. For more information see https://github.com/evermeer/EVReflection/tree/master/Source/CoreData
It will let you write code like this:\
let moc: NSManagedObjectContext = EVReflectionTestsData().moc // Your code for getting the NSManagedObjectContext.
let obj = CoreDataPerson(context: moc, json: "{\"firstName\" : \"Edwin\", \"lastName\" : \"Vermeer\"}")
try! moc.save() //TODO: implement error handling
I use Xcode 8 CoreData with auto generated Base classes.
When I try
let fetchRequest: NSFetchRequest<Event> = Event.fetchRequest()
fetchRequest variable correctly gets type of NSFetchRequest<Event>
When I try
let fetchRequest = Event.fetchRequest()
Xcode tells that fetchRequest has undefined type, as I understand Swift must determine type automatically by making assignment
Here is the auto generated class extension generated by Xcode
extension Event {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Event> {
return NSFetchRequest<Event>(entityName: "Event");
}
#NSManaged public var ffff: String?
#NSManaged public var timestamp: NSDate?
}
As an example this code works correctly (logic is the same)
struct MyStruct<T> {
let myVar: T
}
class MyClass {
}
extension MyClass {
class func test() -> MyStruct<Int> {
return MyStruct<Int>(myVar: 5)
}
}
let b = MyClass.test()
let b has a correct type of MyStruct<Int>
CoreDate automatically generated
#objc(Event)
public class Event: NSManagedObject {
}
extension Event {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Event> {
return NSFetchRequest<Event>(entityName: "Event");
}
#NSManaged public var timestamp: NSDate?
}
NSManagedObject protocol is in Objective-C and it has class method
+ (NSFetchRequest*)fetchRequest
When i try to use let fetchRequest = Event.fetchRequest() it thinks that i call Objective-C + (NSFetchRequest*)fetchRequest and it has no generics
Why doesn't it use an overridden #nonobjc public class func fetchRequest() -> NSFetchRequest<Event> from an Event class extension?