cannot give strong or retain property to protocol object - objective-c

I've the following line of code
#property (nonatomic, retain) id<MessageItemProtocol> *message;
Here, i am getting an error : "property with 'retain' (or strong) attribute must be of object type "
I know that i am getting an error because it's a protocol. so, then what should be it's property type then ??

Try
#property (nonatomic, retain) id<MessageItemProtocol> message;
or
#property (nonatomic, retain) NSObject<MessageItemProtocol> *message;

id already is a pointer, so you do not need *.
#property (nonatomic, retain) id<MessageItemProtocol> message;

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

To Many Relationship in Core Data

This is my GeneratedCode for Core data, automatically generated by xCode Editor.
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Building, Category, City, District, Image, LatitudeLongitude, OpeningHour, Promotion, Rating, Review, URL;
#interface Business : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * Website;
#property (nonatomic, retain) NSString * Email;
#property (nonatomic, retain) NSString * Street;
#property (nonatomic, retain) NSString * InBuildingAddress;
#property (nonatomic, retain) NSString * Phone;
#property (nonatomic, retain) NSString * Title;
#property (nonatomic, retain) NSString * Zip;
#property (nonatomic, retain) NSNumber * Price;
#property (nonatomic, retain) NSSet* Promotions;
#property (nonatomic, retain) Building * Building;
#property (nonatomic, retain) NSSet* Categories;
#property (nonatomic, retain) LatitudeLongitude * LatitudeLongitude;
#property (nonatomic, retain) NSSet* Images;
#property (nonatomic, retain) OpeningHour * OpeningHour;
#property (nonatomic, retain) NSSet* Reviews;
#property (nonatomic, retain) NSSet* URLs;
#property (nonatomic, retain) Rating * Rating;
#property (nonatomic, retain) NSSet* Districts;
#property (nonatomic, retain) City * City;
//I added these 3 lines, why not part of automatically generated Code?
- (void)addDistrictsObject:(District *)value;
- (void)addCategoriesObject:(Category *)value;
- (void)addReviewsObject:(Review *)value;
#end
Say I want to "clear" all Reviews and image.
Will doing self.Reviews=nil do the trick?
I know that doing self.LatitudeLongitude=nil will delete the relationship between self and it's LatitutudeLongitude.
EDIT - Not sure if you mean "clear" as in delete. My answer below is assuming that you want the objects gone from the context; not just severing the relationship between the NSManagedObjects
I don't believe
self.Reviews = nil;
will actually delete your Review objects for the Managed Object Context. That would send the release message to each one, but to delete them from the context you have to call
[aContext deleteObject:reviewObj];
on each one in the set. If you were to delete one of your Business objects (using "deleteObject" as shown above) and you had your relationship delete rules set to "Cascade", then I believe that would cause all of the Review, City, etc... objects owned by that Business object to be deleted automatically, but that is the only other way that I know of that will cause NSManagedObjects to be deleted.

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;

Obj-C selector not recognized on property?

I'm getting a NSObject doesNotRecognizeSelector error when trying to set a property and I'm not sure why.
The error occurs on the first line of setWithNSColor. I'm a bit confused how a property that's properly synthesized could be not recognized.
#interface ScopeColor : NSObject {
NSString *colorIntegerString;
float redColor;
float greenColor;
float blueColor;
NSString *name;
}
#property (readwrite, assign) NSString *colorIntegerString;
#property (readwrite, assign) float redColor;
#property (readwrite, assign) float greenColor;
#property (readwrite, assign) float blueColor;
#property (readwrite, assign) NSString *name;
-(void)setWithNSColor:(NSColor *)inColor
{
self.redColor=[inColor redComponent];
self.greenColor=[inColor greenComponent];
self.blueColor=[inColor blueComponent];
}
Are you sure it is your class and not NSColor that is raising the exception? If the NSColor object does not belong to the NSCalibratedRGBColorSpace or NSDeviceRGBColorSpace asking for redComponent, etc. will raise an exception.
Are you synthesizing redColor, greenColor, and blueColor somewhere outside of the included code? Also, primitive values (such as floats) don't need the assign keyword.