0
votes

I'm trying to work out how to inject resources into NSManagedObject subclass instances, but can't find a reasonable way.

Simply put, I have an object which represents a service that behaviour methods of an Entity require. This service needs to be available at runtime.

In a Plain-Old-Objective-C-Object, I'd simply pass this object as a constructor argument, or set it via a property after construction. Similarly to how many objects require and use a delegate.

However, awakeFromInsert and awakeFromFetch obviously take no parameters, nor can I find anywhere to hook into the NSManagedObjectContext to configure NSManagedObjects post initialisation.

Does anyone have a solution for this?

As a entirely contrived example:

@interface ProductEntity : NSManagedObject

@property (nonatomic, retain) NSNumber *unitPrice;
@property (nonatomic, retain) MyTaxCalculatorService *taxCalculatorService;

- (void)grossPriceForUnits:(NSUInteger)units;

@end

@implementation ProductEntity

@dynamic unitPrice;
@synthesize taxCalculatorService

- (void)grossPriceForUnits:(NSUInteger)units
{
   return [self.taxCalculatorService grossAmountForUnitPrice:self.unitPrice quantity:units];
}

@end

Ignoring whether this is the best way to calculate gross prices (it's a contrived example), how would I get the taxCalculatorService instance into the ProductEntity? I can't override init, and I can't find anywhere I could consistently call [entity setTaxCalculatorService:service].

Thoughts?

3
Are you talking about a single shared instance of the "service" class or does each object have its own instance?jrturton
Every entity has a reference to a service instance. Some may share that, some may not. In my contrived example, you could imagine that some products have a different tax calculation strategy than others.Chris Leishman

3 Answers

1
votes

awakeFromFetch / awakeFromInsert will always be called. You will have to approach it from the other direction - the managed object will have to request the "services" instance from some other class (I think this would be a class method) - something like this in your awakeFrom* methods:

self.serviceProvider = [ServiceProvider getServiceProviderForObject:self];

Your ServiceProvider class would then have all the information about the managed object and could return the appropriate instance.

0
votes

The way it seems, can't you just call the following?

myProductEntityInstance.taxCalculatorService = [[MyTaxCalculatorService alloc] init];

It's defined as a property with retain, so your managed object should keep a reference to it. Note that the changes you make to your managed object won't be made persistant until you call a save on the context the entity is in

0
votes

In my view, by far the cleanest way to do this would be to turn grossAmountForUnitPrice:quantity: into a Class method on your TaxCalculatorService class.

You can then call [TaxCalculatorService grossAmountForUnitPrice:self.unitPrice quantity:units] without any need to instantiate an instance of the taxCalculatorService class.

This assumes that your TaxCalculatorService is not instantiated with state that affects the values it returns, such as a tax rate for a particular locale or similar. If it is, you would of course need that information here in order to instantiate an instance as you're describing, so if that additional information is needed you could just pass that in as an additional parameter on the class method instead.