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

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

Related

How to do deep copying Objective-C

There is a class Patient with properties:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) Symptoms *symptoms;
#property (assign, nonatomic) Status status;
#property (weak, nonatomic) id <PatientDelegate> delegate;
There is a class Symptoms with properties:
#property (assign, nonatomic) CGFloat temperature;
#property (assign, nonatomic) BOOL headache;
#property (assign, nonatomic) BOOL stomach_ache;
Both classes implement protocol NSCopying:
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Patient *newPatient = [[[self class] allocWithZone:zone] init];
[newPatient setName:self.name];
[newPatient setSymptoms:self.symptoms];
[newPatient setStatus:self.status];
[newPatient setDelegate:self.delegate];
return newPatient;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Symptoms *newSymptoms = [[[self class] allocWithZone:zone] init];
[newSymptoms setTemperature:self.temperature];
[newSymptoms setHeadache:self.headache];
[newSymptoms setStomach_ache:self.stomach_ache];
return newSymptoms;
}
Also there is a class Doctor:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSMutableArray *history;
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history;
- (void)report;
When patient gets well, doctor calls method makeNoteIn:
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history {
Patient *newRecord = [patient copy];
[history addObject:newRecord];
}
After the record is made, all property of the patient return to the original values. While we are in the method makeNoteIn and the current patient is proceeded, in history there is link to this object which has correct property values. As soon as we exit the method or start to proceed another patient, all the property values reset to the initial value.
I tried to realise copying, but something still wrong.
When you want to deep-copy an object, you have to implement copy on all the substructures:
[newPatient setName:[self.name copy]];
[newPatient setSymptoms:[self.symptoms copy]];
Otherwise they will still reference the same object and changing one will affect all.
Note that you can do that automatically by declaring the properties as copy:
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) Symptoms *symptoms;
It's common to use copy with NSString and NSArray to prevent assigning NSMutableString and NSMutableArray which could be changed externally by mistake. Make sure you implement NSCopying on Symptoms.

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.

JSONModel - Key is a number, can I get the children by offset?

How do I parse this JSON in Objective C? I've been using jsonmodel.com's code to parse.
{
"found":10958,
"start":3141,
"hits":[
{
"pid":"76493",
"title":"Beton Armu00e9",
"artist":"Raiden",
"genre":"Dubstep",
"image":"A76493_BetonArm_BetonArm.jpg",
"label":"Offkey",
"year":"2011",
"price":9.99,
"release":"Beton Armu00e9",
"type":"Album",
"tracks":{
"0":{
"name":"Barbican",
"file":"A76481_Barbican.mp3",
"tracknum":1,
"pid":"76481"
},
"1":{
"name":"Trinity",
"file":"A76482_Trinity.mp3",
"tracknum":2,
"pid":"76482"
},
"2":{
"name":"Tricorn",
"file":"A76483_Tricorn.mp3",
"tracknum":3,
"pid":"76483"
},
"3":{
"name":"Brutalist",
"file":"A76484_Brutalist.mp3",
"tracknum":4,
"pid":"76484"
},
"4":{
"name":"Trellick",
"file":"A76485_Trellick.mp3",
"tracknum":5,
"pid":"76485"
}
}
}
]
}
JSONModel expects a pointer string to declare the keys, but the keys here are numbers. This is what I need, but won't work:
#import "JSONModel.h"
#import "songParentModel.h"
#protocol albumModel #end
#interface albumModel : JSONModel
#property (strong,nonatomic) NSString *title;
#property (strong,nonatomic) NSString *image;
#property (strong,nonatomic) NSString *artist;
#property (strong,nonatomic) songParentModel *0; // THIS DOESN'T WORK (of course)
#end
I just need to get the first track, but it would be nice to know how to get them all.
My best guess for a solution would be to stop using JSONModel and parse the JSON with some other simplified method.
Create a class like this :
The header:
#import "JSONModel.h"
#protocol Track #end
#interface Track : JSONModel
#property (strong, nonatomic) NSString* name;
#property (assign, nonatomic) NSString* file;
#property (assign, nonatomic) int tracknum;
#property (strong, nonatomic) int pid;
#end
Leave the implementation as default.
now in your model add this property:
#property (strong, nonatomic) NSArray<Track>* allTracks;
And also change the implementation for +(JSONKeyMapper*)keyMapper and add the below item to your dictionary.
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"tracks":#"allTracks",
}];
}
In this way you get an array of all the tracks and you can also get all the details for each track as well.

JSONModel: can't assign fetched JSON to model

My JSON I fetch:
{"username":"example","confirmed_rewards":"5","round_estimate":"0.73605946","total_hashrate":"0","payout_history":"10","round_shares":"85",
"workers":{
"worker.1":{"alive":"0","hashrate":"0"},
"worker.2":{"alive":"0","hashrate":"0"}
}
}
My model:
#import "JSONModel.h"
#protocol LKCoinFCPoolModel #end
#interface LKCoinFCPoolModel : JSONModel
#property (strong, nonatomic) NSString* username;
#property (strong, nonatomic) NSString* confirmed_rewards;
#property (strong, nonatomic) NSString* round_estimate;
#property (strong, nonatomic) NSString* total_hashrate;
#property (strong, nonatomic) NSString* payout_history;
#property (strong, nonatomic) NSString* round_shares;
#property (strong, nonatomic) NSString<Optional> * ErrorCode;
#end
I created the following function which is to fetch a JSON structure and assign it to a model.
-(void)viewDidAppear:(BOOL)animated
{
//show loader view
[HUD showUIBlockingIndicatorWithText:#"Fetching JSON"];
//fetch the feed
LKCoinFCPoolModel* test;
test = [[LKCoinFCPoolModel alloc] initFromURLWithString:#"http://example.com/ap/key"
completion:^(LKCoinFCPoolModel *model, JSONModelError *err) {
//hide the loader view
[HUD hideUIBlockingIndicator];
//json fetched
NSLog(#"user: %#", test.username);
}];}
the problem I'm having is that instead of user: example it prints user: (null).
I'm unsure what I'm doing wrong, it's the first app I'm trying to write in xcode (I'm coming from a Python/Java background).
Your callback is passing the the parsed JSON to you in the LKCoinFCPoolModel *model. In fact I don't think that you can assign test the way you do. If you really wanted to get it into the test model, you should assign it inside the block. Remember that this block runs asynchronous after the JSON is downloaded and parsed. So test won't be valid until then.

Static Instance releasing properties prematurely with ARC

I have a static instance variable that is being used throughout my application. It has properties. These properties are used through my application and seem to work pretty well. However, sometimes the properties are released prematurely. What is odd is that the object that is pulling these properties keeps some and releases others. What would be a good way to insure that the properties of my object are not released prematurely.
Edit: It turns out that the issue was not premature releasing at all. It was a conversion issue. Thanks all for help.
#interface Game : NSObject
#property (nonatomic, strong) PFObject *gameObject;
//#property (nonatomic, strong) Concept *concept; // Will need to add Concept Object to GameObject once it's wrapper is done
#property (nonatomic, strong) User *initialPlayer;
#property (nonatomic, strong) User *invitedPlayer;
#property (nonatomic, strong) User *lastPlayedPlayer;
#property (nonatomic, strong) NSDate *lastPlayedDate;
#property (nonatomic, strong) NSDate *timeOutDate;
#property (nonatomic, assign) int timerTicks;
#property (nonatomic, assign) int currentRoundNumber;
#property (nonatomic, strong) User *winnerPlayer;
#property (nonatomic, assign) int initialPlayerPoints;
#property (nonatomic, assign) int invitedPlayerPoints;
#property (nonatomic, assign) int currentPlayerPoints;
#property (nonatomic, assign) GameStatus status;
#property (nonatomic, assign) int initialPlayerTimeouts;
#property (nonatomic, assign) int invitedPlayerTimeouts;
#property (nonatomic, assign) BOOL isInitialPlayer;
#property (nonatomic, strong) NSMutableDictionary *rounds;
#property (nonatomic, strong, readonly) Round *currentRound;
+(void)getActiveUserGameObjects:(PFUser *)user target:(id)target selector:(SEL)selector;
+(void)getYourTurnGameObjects:(PFUser *)user target:(id)target selector:(SEL)selector;
+(void)getTheirTurnGameObjects:(PFUser *)user target:(id)target selector:(SEL)selector;
+(void)getGameObjects:(PFUser *)user yourTurn:(id)yourTurn target:(id)target selector:(SEL)selector;
+(Game*)currentGame;
+(void)setCurrentGame:(Game*)currentGame;
..
//.m #implementation
..
static Game *sharedInstance = nil;
..
+(Game*)currentGame
{
return sharedInstance;
}
+(void)setCurrentGame:(Game*)currentGame
{
sharedInstance = currentGame;
}
...
#pragma mark - Player Setters and Getters
-(void)setInvitedPlayer:(User *)invitedPlayer
{
if (nil != invitedPlayer.userObject)
{
[self.gameObject setObject:invitedPlayer.userObject forKey:GAME_INVITED_PLAYER];
}
}
-(User*)invitedPlayer
{
NSObject *value = [self.gameObject objectForKey:GAME_INVITED_PLAYER];
if ([value isKindOfClass:[PFUser class]])
{
return [User userFromPFUser:(PFUser*)value];
}
return nil;
}
What would be a good way to insure that the properties of my object are not released prematurely.
Holding onto them by maintaining a strong reference. It is extremely unlikely that ARC is randomly releasing your data. Much more likely is that you are letting go of it when you don't mean to.
The first place I'd look is at your use of setCurrentGame:, making sure that you're not accidentally working on different Game objects at the same time in different parts of the program. First, make sure t
Your +get... methods are awkwardly named (a get prefix means a very specific thing in ObjC, and it's not what you're doing here). Havings class methods that take targets and actions like this seems a likely place to have trouble. It makes me wonder what's going on inside there.
Your conversion between two kinds of User objects is a little suspicious, and I'd make sure you're not accidentally dropping User or PFUser objects when you don't mean to.
Generally speaking, though, this question is over-vague. Are you winding up with dangling strong pointers? Are your strong pointers seeming to become nil? Is your game object itself becoming nil? How do you know when things are being "released?" Or do you mean that they're deallocating? Have you put a breakpoint in dealloc to see who had the last reference to the object?
Make sure your properties are (retain) type (same as strong, I think), and keep an instance refereed to in the app delegate (so that ARC sees the reference as valid for the entire execution of your program).