Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVReflection with NSManagedObject CRASHES! #190

Closed
hernanarber opened this issue Apr 24, 2017 · 15 comments
Closed

EVReflection with NSManagedObject CRASHES! #190

hernanarber opened this issue Apr 24, 2017 · 15 comments

Comments

@hernanarber
Copy link

hernanarber commented Apr 24, 2017

hI: I've been using EVReflection to make our Network Layer Fully Restful and I Must say: AWESOME WORK! Thanks for this Library.

Now, to the Issue:

The Next Step is to get Those Object 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 is 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 /////////////////////////////////////

2017-04-24 17:51:47.195791+0300 engieApp[6570:2198102] [error] error: CoreData: error: Failed to call designated initializer on NSManagedObject class 'engieApp.NGTripSummary'
CoreData: error: CoreData: error: Failed to call designated initializer on NSManagedObject class 'engieApp.NGTripSummary'

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

@evermeer: Please help us! you are the last hope of our Galaxy (dev-team), may the Force be with you :-) 👍

@evermeer
Copy link
Owner

Hmm... This seems like an issue. You should create your objects as you used to do when using a NSManagedObject like using the init with the parameters entity and insertInto. After that you however are able to set the properties from the json by using this:

        let jsonDict = EVReflection.dictionaryFromJson(json)
        EVReflection.setPropertiesfromDictionary(jsonDict, anyObject: yourObject)

But maybe you could also create a designated initialiser on your managedObject class. That would be something like:

    public convenience required  init(json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize, forKeyPath: String? = nil) {
        self.init(entity: XXX, insertInto: YYY) //specify the XXX and YYY
        let jsonDict = EVReflection.dictionaryFromJson(json)
        EVReflection.setPropertiesfromDictionary(jsonDict, anyObject: self, conversionOptions: conversionOptions, forKeyPath: forKeyPath)
    }

@evermeer
Copy link
Owner

I will have a look if I could create a NSManagedObject extension or subclass that will solve this issue. But that will take some time. Please try my suggestion from the comment above. I will keep this issue open so that it keeps visible as a todo item

@hernanarber
Copy link
Author

@evermeer Thanks a LOT for your Fast reply.
We are looking into it and will implement the designated initializer.

Please keep me updated if you modify this to improve NSmanagedObject handling and also Keep up the great Work :-)

👍

@evermeer
Copy link
Owner

If you do have a designated initialiser working and willing to share it, then please let me know.

@hernanarber
Copy link
Author

hernanarber commented May 3, 2017

Hi @evermeer:

We came up with a Designated Initialiser that is Actually Working:

public class NGTripSummary: NSManagedObject {

   // Each NSManagedObject that use EVReflection need to use this init
   // Only copy and change the forEntityName to your className
   public convenience required  init(json: String?,
                                     forKeyPath: String? = nil, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
       
       let entity = NSEntityDescription.entity(forEntityName: "NGTripSummary", in: context)
       self.init(entity: entity!, insertInto: context)
       
       let jsonDictionary = EVReflection.dictionaryFromJson(json)
       let propertiesDictionay:NSMutableDictionary = [:]
       for propertyInObject in (entity?.properties)! {
           if jsonDictionary.allKeys.contains(where: { $0 as! String == propertyInObject.name }) {
              propertiesDictionay.setValue(jsonDictionary[propertyInObject.name], forKey: propertyInObject.name)
           }
       }
       
       EVReflection.setPropertiesfromDictionary(propertiesDictionay, anyObject: self, forKeyPath: forKeyPath)
   }

}

I Hope this helps someone :-)

@evermeer
Copy link
Owner

evermeer commented May 4, 2017 via email

@hernanarber
Copy link
Author

Hi @evermeer:

Yes your code is much more generic, and it Would be nice to have it as an extension for NSManagedObject+EVReflection

However, in our experience, the Properties that were in the JSON and NOT in the Object were not only producing a warning but actually CRASHING...

Anyway, It's great to see that you are keeping this wonderful Library Alive and Updated ;-)

Cheers

@evermeer
Copy link
Owner

evermeer commented May 4, 2017

Ah, true, In order to have full functionality and flexibility all functions from EVObject should be implemented. It's best to create a base class for that and use that instead of NSManagedObject. That's what I will do in the EVReflection subspec. you will then get something like this:

open class EVManagedObject: NSManagedObject, EVReflectable {
    
    /**
     */
    public convenience required init(entityName: String? = nil,
                                     insertIntoManagedObjectContext context: NSManagedObjectContext!,
                                     json: String?,
                                     forKeyPath: String? = nil) {
        let t=type(of: self)
        let name = entityName ?? EVReflection.swiftStringFromClass(t)
        let entity = NSEntityDescription.entity(forEntityName: name, in: context)
        self.init(entity: entity!, insertInto: context)
        
        let jsonDictionary = EVReflection.dictionaryFromJson(json)
        
        EVReflection.setPropertiesfromDictionary(jsonDictionary, anyObject: self, forKeyPath: forKeyPath)
    }
    
    public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
        super.init(entity: entity, insertInto: context)
    }
    
    
    // MARK: - copy of EVObject functions.
    
    // Below is a copy of all functions that are also implemented in EVObject. These are also here because we cannot have multiple enheritence. (Both NSManagedObject and EVObject) Only the init() has to be removed

   //TODO: ...

@evermeer
Copy link
Owner

evermeer commented May 7, 2017

I now have created a CoreData subscpec. It's not yet published to Cocoapods. I first want to create an array initialiser.

Here some information about the subspec:
https://github.com/evermeer/EVReflection/tree/master/Source/CoreData

The test can be found here:
https://github.com/evermeer/EVReflection/blob/master/UnitTests/CoreDataTests/CoreDataTests.swift#L43

@evermeer evermeer added the fixed? label May 7, 2017
@evermeer
Copy link
Owner

evermeer commented May 7, 2017

The array initialiser is now also ready. I am now publishing this as version 4.10.0. In half an hour it's published to Cocoapods.

@hernanarber
Copy link
Author

AWESOME @evermeer! Hey come work with us @ Engie 👍 www.engieapp.com

@evermeer
Copy link
Owner

evermeer commented May 8, 2017

Tel Aviv is a bit far for a daily commute.

@hernanarber
Copy link
Author

BTW: is there a possibility that the GitHub is Updated but the POD is not yet there? We Tried to Pod Update It and it doesn't include the CoreData Folder :-s

@evermeer
Copy link
Owner

evermeer commented May 8, 2017

It should be there you podfile should contain:
pod 'EVReflection/CoreData'
I just downloaded the release tag code 4.10.2 and I do see the CoreData folder
CocoaPods is reporting that the published version is 4.10.2
It's correct that 4.10.0 does not have the CoreData folder. (a publishing mistake)
So it could be that you get the 4.10.0 version?

@evermeer
Copy link
Owner

it looks like sub objects needs to be set using the propertyMapping function. I will add a unit test to show how. I don't see any other easier way to initiate objects with the right context automatically. I will close this issue and create an improvement issue for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants