Subclass NSManagedObject to easy initialize

219 Views Asked by At

For my models from server API i use constructor like that:

@implementation MenuAPIModel

-(instancetype)initWithServerData:(id)data{

    self = [super initWithServerData:data];
    return self;
}

-(void)performMapping{

    _basketId = self.serverData[@"basket_id"];
    _orderId = self.serverData[@"order_id"];
    _itemId = self.serverData[@"id"];
    _name = self.serverData[@"name"] == [NSNull null] ? @"" : self.serverData[@"persons"];
    _payedFrom = [self.dfDetail dateFromString:self.serverData[@"payed_from"]] == nil ? [self.dfBasic dateFromString:self.serverData[@"payed_from"]] : [self.dfDetail dateFromString:self.serverData[@"payed_from"]];
    _payedTill = [self.dfBasic dateFromString:self.serverData[@"payed_till"]] == nil ? [self.dfBasic dateFromString:self.serverData[@"payed_till"]] : [self.dfDetail dateFromString:self.serverData[@"payed_till"]];
    _persons = self.serverData[@"persons"] == [NSNull null] ? @"2-3" : self.serverData[@"persons"];
    _price = self.serverData[@"price"] == [NSNull null] ? @(0) : self.serverData[@"price"];
    _price3 = self.serverData[@"price3"] == [NSNull null] ? @(0) : self.serverData[@"price3"];
    _price12 = self.serverData[@"price12"] == [NSNull null] ? @(0) : self.serverData[@"price12"];
    _status = self.serverData[@"status"] == [NSNull null] ? @"not_payed" : self.serverData[@"persons"];
}

So, i can create object with simply wrote: [MenuAPIModel initWithServerData:data];

Now i subclass NSManagedObject and wonder, how can i subclass this 4 files XCode did create for me, to make constructor like the one above?

2

There are 2 best solutions below

0
On

You can create kind of helper which can have following implementation (it's just example so I used Swift):

static open func createObject <T : NSManagedObject> (_ objectType : T.Type, record : Dictionary<String, Any>? = nil, context : NSManagedObjectContext) -> (T)
{
    let newObject = NSEntityDescription.insertNewObject(forEntityName: String(describing: objectType), into: context)

    // Error never occured

    var typedNewObject = newObject as! T

    guard let trueRecord = record
    else
    {
        return typedNewObject
    }

    typedNewObject = updateObject(typedNewObject, record: trueRecord, context: context)

    return typedNewObject
}

static open func updateObject <T : NSManagedObject> (_ object : T, record : Dictionary<String, Any>, context : NSManagedObjectContext) -> (T)
{
    for (key, value) in record
    {
        setValue(value, key: key, object: object)
    }

    return object
}

static public func setValue (_ value : Any, key : String, object : NSManagedObject) -> (Void)
{
    object.setValue(value, forKey: key)
}

With such methods you can then type:

let myNewObject = Helper.createObject(YourClassName.self, record: yourDictionary, context: yourContext) // Helper, or any other class name you give it.

I hope it's close enough to what you need.

2
On

I don't fully understand what you want to subclass, but in order to use CoreData your models have to subclass NSManagedObject, so if you want to subclass a custom model class that you wrote, that class must subclass NSManagedObject.

Now, the new XCode templates for NSManagedObject creates one class and one category for each model that you declare in xcdatamodel. And you would have something like this.

MenuAPIModel+CoreDataProperties.h

@interface MenuAPIModel (CoreDataProperties) {
}
@property(nonatomic, strong) NSNumber *basketId
.....other properties that you have
@end

MenuAPIModel+CoreDataProperties.m

@implementation MenuAPIModel (CoreDataProperties)
@dynamic basketId
@dynamic ....other properties ...
@end

MenuAPIModel.h

@interface MenuAPIModel : NSManagedObject
+(instancetype)initWithServerData:(id)serverData;
@end

MenuAPIModel.m

@implementation MenuAPIModel

 +(instancetype)initWithServerData:(id)serverData {
     NSManagedObjectContext *context = ...get the current context...
     MenuAPIModel *model = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MenuAPIModel class])inManagedObjectContext:context];
     model.basketId = serverData[@"basket_id"];
     ...other parsing....
     return model;
 }

As you can see, you can use those classes & categories created by default by XCode, by adding just one extra method. Just a reminder, NSManagedObject can't be created using alloc] init];, it has to be created using the NSManagedObjectContext.