How to add a relationship to the parent with Mantle? - ios

I have Parent/child class like this:
#interface Parent : MTLModel <MTLJSONSerializing>
- (void)someMethod;
#property a,b,c...; // from the JSON
#property NSArray *childs; // from the JSON
#end
#interface Child : MTLModel <MTLJSONSerializing>
#property d,e,f,...; // from the JSON
#property Parent *parent; // *not* in the JSON
#end
All the fields a to f are in the JSON, with the same name (hence my JSONKeyPathsByPropertyKey method return nil), and the proper JSONTransformer is correctly setup so that the childs array in parent is containing Child class and not NSDictionary.
Everything work forwardly.
But I want, as a convenience, a property in my Child model that reference back to the parent that own it. So that in the code I can do that:
[childInstance.parent someMethod]
How do I do that with Mantle ??
I want, when the parent is parsing the child's JSON and creating the Child class, to add a ref to itself. (With an init method ??)
Thanks.

I do this by overriding MTLModel -initWithDictionary:error: method. Something like this.
Child interface:
#interface BRPerson : MTLModel <MTLJSONSerializing>
#property (nonatomic, copy, readonly) NSString *name;
#property (strong, nonatomic) BRGroup *group; // parent
#end
In parent implementation:
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error {
self = [super initWithDictionary:dictionaryValue error:error];
if (self == nil) return nil;
// iterates through each child and set its parent
for (BRPerson *person in self.people) {
person.group = self;
}
return self;
}
Techinical note:
If you are curious like me, I already tried to tweak MTLJSONAdapter by changing its forwardBlock and reversibleBlock. But I can't, because those are inside MTLReversibleValueTransformer superclass, and that class is declared privately in "MTLValueTransformer.m". So the initWithDictionary approach above should be much easier.

Related

Objective-C Implementing method in Base class with the child class type property

I have some code that I am trying to accomplish. I have a base class and some properties that are same for my child classes. Getter and setters needs to be implemented. In my child class I have a property and reference to it, ExampleType &type. ExampleType parent is BaseType witch cannot be instanced. Getter and setters in base class from my properties depend upon BaseType. So if I have in my base class something like this BaseType *type. Example of my getter:
-(NSString *) property {
return self.type->returnString;
}
This really depends on my child property type ExampleType.I do not want to have copy/paste code in my child classes for properties. Does anyone know how to accomplish this?
I think I know what you wanted to accomplish. Here are the type classes:
#interface BaseType : NSObject
#property (nonatomic, strong) NSString* returnString;
#end
#implementation BaseType
#end
#interface ExampleType : BaseType
#end
#implementation ExampleType
- (NSString *)returnString
{
return #"returnString from ExampleType";
}
#end
And here are the main objects, incapsulating the type classes:
#interface Parent : NSObject
#property (nonatomic, strong) NSString* property;
#end
#implementation Parent
- (BaseType*)type
{
NSAssert(NO, #"should be implemented in child class");
return nil;
}
- (NSString *)property
{
return self.type.returnString;
}
#end
#interface Child : Parent
#property (nonatomic, strong) ExampleType* type;
#end
#implementation Child
#end
So now you have a property ExampleType in your child class, and the property implementation is present only in the parent class.

best practice for how to inherit property from parent class and override setter and getter

I am trying to re-struct my project code with an inheritance style, what is the best practice for how to inherit property from parent class and override setter and getter?
I give the demo code, in the demo, ChartModel is a base class in a ChartViewController, and LineChartModel is a sub class of ChartModel in LineChartViewController.
I want to override the setter and getter of LineChartModel *dataModel in sub view controller. Please include any #synthesize and protected instance variable, or if it is automatically generated by compiler, please mark. Thank in advance.
// ChartModel.h
#interface ChartModel : NSObject
-(BOOL)hasData;
#end
// LineChartModel.h
#interface LineChartModel : chartModel
-(void)getLineColor;
#property (nonatomic, strong) NSArray* dataArray;
#end
// ChartViewController.h
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
-(void)updateUI;
#end
// ChartViewController.m
#implementation ChartViewController
-(void)updateUI {
if ([self.dataModel hasData]) {
[self.view setHidden:NO];
} else {
self.view.hidden = YES;
}
// setter and getter here
#end
// LineChartViewController.h
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
#end
// LineChartViewController.m
#implementation LineChartViewController
//override dataModel setter here
//override dataModel getter here
#end
Technically, the only thing you need in the implementation of LineChartViewController is:
#dynamic dataModel;
That tells the compiler that the getter and setter will be supplied in some way it can't immediately see. In actuality, they will be supplied by the superclass.
However, that allows for a problem. A LineChartViewController is-a ChartViewController. That means that an instance of LineChartViewController can be passed to a method or function which is declared to take a ChartViewController and that method/function is entitled to do anything to it that is allowed by the interface of ChartViewController. That includes assigning an instance of ChartModel (not LineChartModel) to its dataModel property. Presumably, LineChartViewController will break if its dataModel property is not a LineChartModel.
In technical terms, your design violates the Liskov substitution principle.
It's not a fix for the design issue, but you can catch the problem at run time if it happens, by implementing an override of the setter like this:
- (void) setDataModel:(LineChartModel*)dataModel
{
if (dataModel && ![dataModel isKindOfClass:[LineChartModel class]])
{
NSString* reason = [NSString stringWithFormat:#"%# is not a valid dataModel for LineChartViewController; it must be a kind of LineChartModel", dataModel];
[[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
}
[super setDataModel:dataModel];
}

How to declare array in object and store with encode

I am creating a class Ticket. In that ticket I want a mutable array of NSStrings
i.e. in ticket.h
#interface Ticket : NSObject
#property NSString *ticketName;
#property NSMutableArray *games;
However Objective C doesn't allow me to do this. What am I supposed to do to have an array inside an object?
I then want to store that array using encodeWithCoder in the implementation of the object
like i said you might have a syntax problem, there is no reason why Objective-c won't allow you to add an NSMutableArray into your custom objects, try this:
#interface MyObject : NSObject
#property (nonatomic, strong) NSMutableArray *myMutableArray;
//
// .. other properties
//
#end
and in the implementation
#implementation MyObject
- (void)viewDidLoad
{
[super viewDidLoad];
[self.myMutableArray addObject:#"myString1"];
[self.myMutableArray addObject:#"myString2"];
[self.myMutableArray addObject:#"myString3"];
}

objective-c rename/override parent property?

Is there an easy way to rename a parent's property or have a new property reuse an existing one?
For example, I have a parent class defined as such:
#interface Parent : NSObject
#property NSString* Name;
#end
If I want a child class to use that Name property but to call it something else, I know I can do something like this:
#interface Child : Parent
#property NSString* ChildName;
#end
#implementation Child
- (void) setChildName:(NSString *)ChildName
{
[super setName:ChildName];
}
- (NSString *) ChildName
{
return [super Name];
}
#end
But is there an easier way of doing this with getter/setter? This didn't work for me:
#interface Child : Parent
#property (getter=Name, setter=setName) NSString* ChildName;
#end
Oren is correct, you've got some work to do with understanding properties, but you could try setting the getter/setter like this:
#interface Child : Parent
#property (getter=childName, setter=setChildName) NSString* Name;
#end
You will likely want to add (strong, nonatomic) in your property description as well (or whatever would be appropriate for your context).

How to get View object to respond to changes to its data source

Say I have a CatModel object:
#interface CatModel : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, strong) UIImage *catImage;
- (void)addWhisker:(Whisker*)whisker;
And I have a CatView:
#interface CatView : UIView
#property (nonatomic, strong) CatModel *dataSource;
#end
I want the CatView to automatically add a whisker image when it detects that a whisker was added to its dataSource. I don't want to add a Whisker to the CatModel instance and also add an addWhisker method to the CatView. What's the best way to do this?
Sounds like you may use key-value observing, but unfortunately, NSArray or any other collections are not KVO-compatible.
Instead, you may wish to create a delegate functionality:
#protocol CatModelObserving
#optional
- (void)catModel:(CatModel *)model didAddWhisker:(Whisker *)whisker;
#end
...
#interface CatModel
#property (weak, nonatomic) id <CatModelObserving> observer;
#end
Then you would ensure that the CatView conforms to that protocol and implements that method:
- (void)catModel:(CatModel *)model didAddWhisker:(Whisker *)whisker {
// handle it properly
}
In your CatModel.m, inside the -addWhisker: method you should notify the observer that a whisker has been added:
if (self.observer && [self.observer respondsToSelector:#selector(catModel:didAddWhisker:)]) {
[self.observer catModel:self didAddWhisker:whisker];
}
If you wish to have multiple "observers", you may consider using GCD and block-based "notifications", like so:
[catModel addDidAddWhiskerBlock:^(Whisker *whisker) {
// handle it properly
}];
But I will not discuss that method in this answer. As a hint I can suggest using NSMutableArray storing all those blocks, then iteratiating through those blocks in the -addWhisker: method and calling each block.

Resources