Unrecognized selector sent to instance: key-value pair - objective-c

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

Related

Property requires method to be defined in NSManagedObject subclass

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

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.

How can I cast my NSURLSessionDownloadTask to my custom NSURLSessionDownloadTask (inheritance)?

I have created a custom NSURLSessionDownloadTask named VJSessionTask and I have just added some custom things like a type (enum) and a custom object (id):
#interface VJSessionTask : NSURLSessionDownloadTask
typedef enum types
{
LS, LSH, DL, UL, RM, TH
} type;
#property enum types type;
#property (strong, nonatomic) id customObject;
#property (strong, nonatomic) NSString *progressNotif;
#property (strong, nonatomic) NSString *doneNotif;
#property (strong, nonatomic) NSURL *tmpFile;
#end
And when I do this:
VJSessionTask *taskSession = (VJSessionTask *)[self.prioritySession downloadTaskWithRequest:listFileRequest];
// init taskSession with its type
taskSession.type = LS;
I get this error:
-[__NSCFLocalDownloadTask setType:]: unrecognized selector sent to instance 0x1556198f0
Then I come to you as I don't understand or I don't know how to do that...
Thank you in advance ;)
NSURLSessionTasks are not strictly speaking subclass-able unfortunately. This is evident in that the system can queue a data task and return a NSCFLocalDownloadTask (presumably meaning that the task will return its content from the cache).
The best way to go about doing this is to borrow on from the architectural decision of AFNetworking and have individual taskDelegates that monitor all the responses an individual task works on. Then when you want to find the data relating to a task you can query your dictionary of taskDelegates. Each task has a unique identifier that you can use to key your dictionary with.
In AFNetworking you can see the taskDelegate is defined as follows:
#interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
#property (nonatomic, weak) AFURLSessionManager *manager;
#property (nonatomic, strong) NSMutableData *mutableData;
#property (nonatomic, strong) NSProgress *progress;
#property (nonatomic, copy) NSURL *downloadFileURL;
#property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
#property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
#end
#implementation AFURLSessionManagerTaskDelegate
and subsequently retrieved as follows:
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[#(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
See this post for more info

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;