NSDictionary: Comparing NSDictionaries - objective-c

There are two of files. Lets call them fileOne and fileTwo
Each has several NSMutableDictionary properties with identical names. To list a few:
#property (retain, nonatomic) NSMutableDictionary * lunchStartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity1NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity2NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationAddressObject;
I would like to compare the dictionaries with the same name in the two files by calling the method below (or something similar):
-(NSMutableDictionary *)cellColorForChanges:(NSMutableDictionary *)newdictionary :(NSMutableDictionary *)oldDictionary;
{
if(![newdictionary isEqualToDictionary:oldDictionary])
{
[newdictionary setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
return newdictionary;
}
I'm trying avoid writing code for each NSMutableDictionary manually. Is there a way to avoid the following:
if(![fileOne.lunchStartTimeObject isEqualToDictionary:fileTwo.lunchStartTimeObject])
{
fileOne.lunchStartTimeObject setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
I'm having trouble figuring out the most efficient way to accomplish the above. Is it somehow possible to send each dictionary to a method and get back the dictionary (updated with another key, if it's not equal)? Or what I'm trying to avoid is unavoidable?

You are running into this problem because you are not abstracting your objects sufficiently. It seems to me that your property list above is ridiculously redundant. Even the simple code code you provided is hardly readable.
Try to think of your problem in a more conceptional way. Try to think of objects that could encapsulate the functionality you are looking for.
Looking at your properties you probably want a class like this
#interface Activity : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSDate *startTime;
#property (nonatomic, strong) Location *location;
-(BOOL)isEqualToActivity:(Activity*)activity;
#end
Maybe you need a location class that stores more information about a location; perhaps you could use even more fields like firstName, lastName; maybe you need a type (enum or string) property that tells what kind of activity it is, etc. -- you get the idea.
As indicated, you could write your own comparison method where you could tweak for allowing more or less strict capital or small letters, number formats etc.

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.

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

CoreData How to connect with relationship entity to another entity with already preloaded data?

This is my first application in CoreData so I definitely missing something here, but I spend over a week trying to find solution and apparently nobody on Internet had the same problem ;)
I'm creating something like deckbuilder app.:
My model looks like this: coredatamodel
The relationship is to-one and inverse to-many ( every card can be in one set, and in every set can be many cards) from CARD to SET.
For sake of simplicity I want to focus on CARD and SET entities.
I preloaded data into SET from CSV file into 5 atributes - didnt fill relationship "karty" because I didn't know how to do this. There are about 25 records in this entity.
And now, problem is I am trying to fill CARDS Entity which will have over 500 records.
Trying few options I found on SO I end with connection that created 500 records in SET too:/ so it looked like sql "JOIN" command.
What I want is load data to CARDS and connect them somehow to SET not changing number of records in SET.
If i have property (nonatomic, retain) NSSet *karty; what exactly NSSet means? It is s et but set of what? Set of single rows from Card entity? Set of Card objects? Set of NSStrings? Set of NSManagedObjects?
In normal SQL primary key to connect them would be "nazwaszort" so Card.nazwaszort=Set.nazwaszort.
My Set.h
#class Card;
#interface Set : NSManagedObject
#property (nonatomic, retain) NSNumber * cykl;
#property (nonatomic, retain) NSNumber * czymajor;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwashort;
#property (nonatomic, retain) NSNumber * nrwcyklu;
#property (nonatomic, retain) NSSet *karty;
#end
#interface Set (CoreDataGeneratedAccessors)
- (void)addKartyObject:(Card *)value;
- (void)removeKartyObject:(Card *)value;
- (void)addKarty:(NSSet *)values;
- (void)removeKarty:(NSSet *)values;
#end
My Card.h
#class Frakcja, Kolekcja, Set;
#interface Card : NSManagedObject
#property (nonatomic, retain) NSNumber * czylimit;
#property (nonatomic, retain) NSString * frakcja;
#property (nonatomic, retain) NSString * icesila;
#property (nonatomic, retain) NSNumber * iloscwsecie;
#property (nonatomic, retain) NSNumber * influence;
#property (nonatomic, retain) NSString * kodkarty;
#property (nonatomic, retain) NSNumber * koszt;
#property (nonatomic, retain) NSNumber * minimumdecksize;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwasetu;
#property (nonatomic, retain) NSString * nazwaszort;
#property (nonatomic, retain) NSNumber * nrcyklu;
#property (nonatomic, retain) NSString * podtyp;
#property (nonatomic, retain) NSString * strona;
#property (nonatomic, retain) NSNumber * trashkoszt;
#property (nonatomic, retain) NSString * typ;
#property (nonatomic, retain) Kolekcja *ilewkolekcja;
#property (nonatomic, retain) Frakcja *nazwafrakcji;
#property (nonatomic, retain) Set *wjakimsecie;
#end
I preloaded cards to SET using method
-(void) preloadDataPackInfoToDatabase {
NSError *error=nil;
NSString *sciezka = [[NSBundle mainBundle]pathForResource:#"nrsets" ofType:#"csv"];
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:sciezka];
for (int i=1; i <=([rows count]-1); i++) {
Set *nowyDataPack = [NSEntityDescription insertNewObjectForEntityForName:[entityset name] inManagedObjectContext:_contextdp];
NSLog(#"tablica wierszy %#",rows[i][2]);
NSString *koddodatku = rows[i][2];
NSLog(#"kod dodatku:%#",koddodatku);
NSString *nrwcyklu = rows[i][4];
NSString *nrcyklu = rows[i][3];
NSString *nazwadatapack =rows[i][3];
NSString *czymajor =rows[i][0];
[nowyDataPack setValue:nazwadatapack forKey:#"nazwa"];
NSNumberFormatter *f = [[NSNumberFormatter alloc]init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *nrwcykluint = [f numberFromString:nrwcyklu];
NSNumber *nrcykluint = [f numberFromString:nrcyklu];
NSNumber *czymajorbool = [f numberFromString:czymajor];
[nowyDataPack setValue:nrwcykluint forKey:#"nrwcyklu"];
[nowyDataPack setValue:nrcykluint forKey:#"cykl"];
[nowyDataPack setValue:koddodatku forKey:#"nazwashort"];
[nowyDataPack setValue:czymajorbool forKey:#"czymajor"];
}
if (![ self.contextdp save:&error]) {
NSLog(#"Nieznany błąd %#,%#",error,[error userInfo]);
}
}
And another method (on pastebin to not flood your screens --> preloadAllCardsToDatabase
Thanks in advance for any help.
OK I finally did it. Had to move some CoreData stuff outside loop.
Still dont understand this whole magic but at least it works.
for those interested this is my preloading function --> here

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.

Coding convention for space after pointer * [duplicate]

This question already has an answer here:
Convention for pointer *
(1 answer)
Closed 9 years ago.
In Cocoa, what's the best convention on writing the * for pointer?
#property (nonatomic, retain) MyClass * instance;
or
#property (nonatomic, retain) MyClass *instance;
I noticed that the auto-generated code for Core Data, puts the *instance by relationship, but * instance by property:
#property (nonatomic, retain) NSDate * send;
#property (nonatomic, retain) NSString * text;
#property (nonatomic, retain) User *user;
#property (nonatomic, retain) User *userManager;
It's better to use
char *examplea;
For cases like such:
Say you'd like to include multiple pointer variables in one line like so, you'd need to do so like this:
char *examplea, *exampleb;
Almost all of the Objective-C code I've already read does it like this:
#property (nonatomic, retain) MyClass *instance
Personally, I also think it looks the cleanest/best that way