Property requires method to be defined in NSManagedObject subclass - objective-c

I have some protocol like this:
#protocol UserProtocol <NSObject>
#property (nonatomic, strong) NSNumber *uid;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSNumber *rating;
#end
Then I created some actual class that implements that:
#interface User : NSObject <UserProtocol>
#end
Now I need another implementation that uses CoreData so I created CDUser entity (Xcode also generates category for that):
// CDUser.h
#interface CDUser : NSManagedObject <UserProtocol>
#end
// CDUser+CoreDataProperties.h
#interface CDUser (CoreDataProperties)
#property (nullable, nonatomic, retain) NSNumber *uid;
#property (nullable, nonatomic, retain) NSString *name;
#property (nullable, nonatomic, retain) NSNumber *rating;
#end
// CDUser+CoreDataProperties.m
#implementation CDUser (CoreDataProperties)
#dynamic uid;
#dynamic name;
#dynamic rating;
#end
CDUser actually implements UserProtocol but I have warnings like so for all properties:
Property 'uid' requires method 'uid' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation
If I add #dynamic uid; again in CDBook.m then I get the following error:
Property declared in category 'CoreDataProperties' cannot be implemented in class implementation
How can I solve these warnings in a proper way?

Cause CDUser doesn't implement this protocol. Use protocol on category instead.
#interface CDUser : NSManagedObject
#end
// CDUser+CoreDataProperties.h
#interface CDUser (CoreDataProperties) <UserProtocol>
#property (nullable, nonatomic, retain) NSNumber *uid;
#property (nullable, nonatomic, retain) NSString *name;
#property (nullable, nonatomic, retain) NSNumber *rating;
#end

Related

Assigned to Readonly Property Objective-C

I am writing a unit test to test a method that updates a checklist. The checklist has these properties:
typedef NS_ENUM (NSUInteger, ChecklistStatus) { Pending, Completed };
#protocol IChecklistItem <NSObject>
#property (nonatomic, assign, readonly) NSInteger Id;
#property (nonatomic, copy, readonly) NSString *Description;
#property (nonatomic, assign, readonly)BOOL IsCompleted;
#property (nonatomic, assign, readwrite) ChecklistStatus Status;
#property (nonatomic, strong, readwrite) NSDate *CompletedDate;
#property (nonatomic, copy, readwrite) NSString *CompletedByUserId;
#property (nonatomic, assign, readonly) NSInteger RoleId;
#property (nonatomic, assign, readonly) NSInteger GroupId;
#property (nonatomic, strong, readonly) NSArray<IChecklistNote> *Notes;
- (void)sortNotes;
#end
However, in my unit test, as I am trying to validate,
checklistItem.Description = #"hello";, I get the error"Assignment to readonly property"
Why is this so?
heres the rest of my test method:
- (void)testUpdateChecklist {
NSString *testChecklistId = #"1";
NSString *testPatientDescription = #"Descriptive Description";
// What other properties do I need here?
XCTAssertNotNil(_service);
__block CCChecklistItem *checklistItem = nil;
SignalBlocker *blocker = [[SignalBlocker alloc] initWithExpectedSignalCount:1];
id delegate = OCMProtocolMock(#protocol(ChecklistServiceDelegate));
OCMExpect([delegate didCompleteUpdateChecklistItem:[OCMArg checkWithBlock:^BOOL(id obj) {
checklistItem = obj;
XCTAssertNotNil(checklistItem);
[blocker signal];
return true;
}]]);
[_service updateChecklistItem:checklistItem delegate:delegate];
[blocker waitWithTimeout:5.0f];
OCMVerifyAll(delegate);
NSString *originalDescription = checklistItem.Description;
checklistItem.Description = #"hello";
}
EDITED QUESTION:
So when I change the property from above to ReadWrite, I get this error in CChecklistItem
#interface CCChecklistItem ()
#property (nonatomic, assign, readwrite) NSInteger Id;
#property (nonatomic, copy, readwrite) NSString *Description;
#property (nonatomic, assign, readwrite) NSInteger RoleId;
#property (nonatomic, assign, readwrite) NSInteger GroupId;
#property (nonatomic, strong, readwrite) NSMutableArray<IChecklistNote> *Notes;
#end
`Illegal redeclaration of readwrite property in class extension 'CChecklistItem'
Your property is set to readonly as seen here:
#property (nonatomic, copy, readonly) NSString *Description;
Change it to:
#property (nonatomic, copy) NSString *Description;
or if you want to be consistent with the other properties (though overly explicit, IMO):
#property (nonatomic, copy, readwrite) NSString *Description;
Changing scope visibility only to satisfy tests is not encouraged. The easiest solution in your case would be to take advantage of wonderful KVO which Objective-C gives you.
Translated to your original question it would be something like:
[checklistItem setValue:#"hello" forKey:#"Description"]
No need to change access modifiers and your tests will be fine.
Your property is declared readonly in the protocol that the class CChecklistItem conforms. When that property is then synthersized it will create the backing variable and a getter method -(NSString *)description; but no setter method, since it is readonly. So redeclaring it as readwright in your anonymous category, that i'm guessing is declared in your test file to expose private methods to the test case, won't work since there still is no setter method for the property. Further more, even if you decide to try to make your own setter in the implementation of a category on your class you can't since there is no way to access the variable _description that is only exposed in the CChecklistItem.m file.
Depending on what you need to do with your test it might work to stub the getter - (NSString *)description; and return your #"hello" string when that method is called instead of trying to set the actual value to the backing variable.

Unrecognized selector sent to instance: key-value pair

I get
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM metadata]: unrecognized selector sent to instance 0xd0b6250'
when i reach and execute this part of my code:
IDTherapyMetadata* meta = [self.dataCon getMetaDataFromId:content.metadata];
the method in dataCon looks like this:
-(IDTherapyMetadata*)getMetaDataFromId:(NSString*)metaDataId
{
for (IDTherapyMetadata* metadata in self.parser.metadata)
{
if([metaDataId compare:metadata.objId] == NSOrderedSame)
{
return metadata;
}
}
return nil;
}
The strange thing is, if I write
IDTherapyMetadata* meta = [self.dataCon getMetaDataFromId:[content valueForKey:#"metadata"]];
instead it works just fine.
However I want to be able to use dotnotation and looking through apple's documentation
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/KeyValueCoding/Articles/BasicPrinciples.html
haven't helped me.
Why i'm I getting this behaviour and how do I fix it?
Any help is greatly appreciated
EDIT:
The code for IDTherapyContents.h:
#import <Foundation/Foundation.h>
#interface IDTherapyContents : NSObject
#property (nonatomic, copy)NSString* urlId;
#property (nonatomic, copy)NSString* url;
#property (nonatomic, copy)NSString* type;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* metadata;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
Code for IDTherpayMetaData.h
#import <Foundation/Foundation.h>
#import "IDTherapyImages.h"
#interface IDTherapyMetadata : NSObject
//Array contains 'String' objects
#property (nonatomic, strong)NSMutableArray* thumbnails;
#property (nonatomic, copy)NSString* title;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* subCategory;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
#property (nonatomic, copy)NSString* description5;
#property (nonatomic, copy)NSString* description4;
#property (nonatomic, copy)NSString* description3;
#property (nonatomic, copy)NSString* description2;
#property (nonatomic, copy)NSString* description1;
#property (nonatomic, copy)NSString* category;
//Array contains 'IDTherapyImages' objects
#property (nonatomic, strong)NSMutableArray* images;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
Code for IDTherapyProducts.h:
#interface IDTherapyProducts : NSObject
//Array contains 'String' objects
#property (nonatomic, strong)NSMutableArray* contents;
#property (nonatomic, copy)NSString* uniqueId;
#property (nonatomic, copy)NSString* timestamp;
#property (nonatomic, copy)NSString* price;
#property (nonatomic, copy)NSString* posted;
#property (nonatomic, copy)NSString* normalPrice;
#property (nonatomic, copy)NSString* metadata;
#property (nonatomic, copy)NSString* lang;
#property (nonatomic, copy)NSString* objId;
#property (nonatomic, copy)NSString* badge;
//methods
-(id)initWithJsonDic:(NSDictionary*)jsonDic;
#end
This is a simple case of confusing properties with KVC. They are different things.
content.metadata is nothing more than syntactic sugar for [content metadata]. So you need a method on content called -metadata and as content is an NSDictionary that method doesn't exist. So that's why the exception is thrown.
On any normal object [content valueForKey: #"metadata"] would also throw an exception because KVC looks for a method with that name. However, on NSDictionary -valueForKey: is overridden to call -objectForKey: unless the key starts with an # in which case the # is stripped and [super valueForKey: ...] is invoked.
However I want to be able to use dotnotation
Then you need a method on NSDictionary called -metadata. You could use a category.
#interface NSDictionary(MyMetadataExtension)
-(id) metadata;
#end
#implementation NSDictionary(MyMetadataExtension)
-(id) metadata
{
return [self objectForKey: #"metadata"];
}
#end

object property in objective-c

#interface Category : NSObject
#property(nonatomic) NSInteger ID;
#property(nonatomic, retain) NSString *Name;
#property(nonatomic, retain) NSString *Description;
#end
product class
#interface Product : NSObject
#property(nonatomic) NSInteger ID;
#property(nonatomic, retain) NSString *Name;
#property(nonatomic, retain) NSString *Description;
#property(nonatomic, retain) Category *category;
#end
How do I create a property of type Category. Is it possible at all?
You do it just like you have, except you need to add a forward declaration of your Category class in Product.h (before your #interface line):
#Class Category;
And then import the header in Product.m
#import "Category.h"
1) If you class Product is defined in separate file, then:
#import "Category.h" // file, where class Category is defined
#interface Product : NSObject
#property(nonatomic) NSInteger ID;
#property(nonatomic, retain) NSString *Name;
#property(nonatomic, retain) NSString *Description;
#property(nonatomic, retain) Category *category;
#end
2) If your classes Product and Category are defined in same file then you don't need any additional import but be sure that class Categiry is defineed earlier then Product.
3) In the case when two classes have objects of type of each other then your should use #class tag:
#class Product;
#interface Category : NSObject
#property(nonatomic, retain) Product *product;
#end
#interface Product : NSObject
#property(nonatomic, retain) Category *category;
#end
Yes, it is possible, and the syntax that you have posted is correct. If you are having a problem, perhaps it may be because you need to do
#import "Category.h"
at the top of Product.h, and you will need to make sure to #synthesize category. But since you have been using other properties, I assume you knew that already.

Properties declared as instance variables too?

This code works:
#interface StringStuff : NSObject {
}
#property (nonatomic, retain) NSString *String1;
#property (nonatomic, retain) NSString *String2;
- (NSString *) doSomethingWithStrings;
#end
But I often see:
#interface StringStuff : NSObject {
NSString *String1;
NSSTring *String2;
}
#property (nonatomic, retain) NSString *String1;
#property (nonatomic, retain) NSString *String2;
- (NSString *) doSomethingWithStrings;
#end
Is there a reason that properties are often declared as an instance variable as well? Is it just considered good form?
Legacy; it used to be (and still is on 32 bit Mac OS X targeted code) that the ivar declarations were required. That is no longer true on iOS, the simulator and 64 bit OS X.
Note that it is common to #synthesize iVar = iVar_; to prevent accidental direct access where self.iVar is really required.

Expected specifier-qualifier-list before '*' token Objective C

Would anyone be kind enough to explain to me what I'm doing wrong. I'm giving it :
#import <Foundation/Foundation.h>
#interface Product : NSObject {
NSString *imageAddress;
NSString *name;
NSString *title;
}
#property (nonatomic, retain) *imageAddress;
#property (nonatomic, retain) *name;
#property (nonatomic, retain) *product;
#end
and its giving me:
Expected specifier-qualifier-list
before '*' token
for the property calls.
Thanks
This is gcc's cryptic way of telling you that you need a type for your properties.
#property (nonatomic, retain) NSString *imageAddress;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *product;