Mapping an NSArray of NSDate with RKObjectMapping - objective-c

Suppose I have this Json :
{ "arrayOfDates" : [ "7-28-2013", "7-29-2013", "7-30-2013"]}
And my object is :
#interface MyObject : NSObject
#property (nonatomic, retain) NSArray* dates;
#end
I tried to map the arrayOfDates with dates.
RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[MyObject class]];
[mapping addAttributeMappingsFromDictionary:#{#"arrayOfDates" : #"dates"}];
NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateFormat:#"MM-dd-yyyy"];
mapping.preferredDateFormatter = dateFormatter;
The mapping result was an array of NSString !
Is that a way to get an array of NSDate instead of NSString ?

You can't get RestKit to do it. Instead, iterate over dates in the completion block called by RestKit and use your formatter to convert the strings, then update dates.
RestKit usually translates the string to a date if the destination property is an NSDate, but in your case it's an array so RestKit doesn't know that it should be transformed.

I came across this answer trying to solve a similar problem. I found a way to get RestKit to do this using a custom RKValueTransformer.
RKObjectMapping* objectMapping = [RKObjectMapping mappingForClass:[MyObjectWithAnNSArrayProperty class]];
RKAttributeMapping* datesAttributeMapping = [RKAttributeMapping attributeMappingFromKeyPath:#"arrayOfDates" toKeyPath:NSStringFromSelector(#selector(myArrayOfDates))];
datesAttributeMapping.valueTransformer = [RKBlockValueTransformer
valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass)
{
// We transform a `NSArray` into another `NSArray`
return ([sourceClass isSubclassOfClass:[NSArray class]] &&
[destinationClass isSubclassOfClass:[NSArray class]]);
}
transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, Class outputValueClass, NSError *__autoreleasing *error)
{
// Validate the input and output
RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSArray class], error);
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, [NSArray class], error);
NSArray* inputValueArray = inputValue;
NSMutableArray* result = [[NSMutableArray alloc] initWithCapacity:inputValueArray.count];
// Convert strings to dates
for (NSString* inputDate in inputValueArray)
{
[result addObject:RKDateFromString(inputDate)];
}
// Get a non-mutable copy
*outputValue = [result copy];
return YES;
}];
[objectMapping addPropertyMapping:datesAttributeMapping];

Related

Serialization Objective C not working

I'm trying to make an app with Objective C.
I'm trying to serialise an array existing out of objects and after wards deserialise it. Inside the object there are the methods
(id)initWithCoder:(NSCoder *)coder` and `encodeWithCoder:(NSCoder *)coder
But it seems the "rootObject" stays "nil" in the "loadDataFromDisk" -method
Here is my code :
#import "Alarm.h"
#implementation Alarm
#synthesize array = _array;
#synthesize time = _time;
#synthesize coder = _coder;
-(id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self)
{
_array = [coder decodeObjectForKey:#"array"];
_time = [coder decodeObjectForKey:#"time"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.array forKey:#"array"];
[coder encodeObject:self.time forKey:#"time"];
}
#end
My save and load methods :
-(void)saveDataToDisk
{
NSString * path = [self pathForDataFile];
NSLog(#"Writing alarms to '%#' %lu", path, (unsigned long)array.count);
NSMutableDictionary * rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:array forKey:#"alarms"];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path];
}
-(void)loadDataFromDisk
{
NSString *path = [self pathForDataFile];
NSDictionary *rootObject = [NSMutableDictionary dictionary];
rootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// "array" is an array with Objects of "Alarm"
array = [rootObject valueForKey:#"alarms"];
NSLog(#"Loaded from : %# %lu",path ,(unsigned long)array.count);
}
I hope anyone can help me out with this.
Thanks in advance.
You #synthized the array backing store (ivar) as _array. So you need to access the array as either _array or self.array. In saveDataToDisk and loadDataFromDisk it is accessed as array.
To test your array coding try something simple like this:
NSLog(#"array: %#", self.array);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.array];
NSLog(#"data: %#", data);
NSArray *recovered = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"recovered: %#", recovered);
Note: There is no need to wrap your array in a NSMutableDictionary.
When that works change it to the file based method calls.
Check that the filePath is valid.
Check that the file is created.
Check that the file contents are the same as in the above test code.
Note: There is no reason to wrap your array in a NSMutableDictionary.

Can't access NSDictionary values

I have an NSDictionary called equiposDic, which I need to retrieve using a value from another NSDictionary (value dip from ) but I can't' access it:
equiposDic = [[NSDictionary alloc] initWithObjectsAndKeys:#"FC Barcelona Regal", #"49", #"Real Madrid", #"50", #"Caja Laboral", #"51", #"Banca CĂ­vica", #"52", #"Gescrap Bizkaia",#"53", #"Valencia Basket", #"13",#"Lucentum Alicante",#"54",#"Lagun Aro GBC",#"4",#"CAI Zaragoza", #"55", #"Assignia Manresa",#"2", #"FIATC Mutua Joventut",#"8",#"Unicaja",#"56",#"Gran Canaria",#"57",#"Mad-Croc Fuenlabrada",#"9",#"Blusens Monbus",#"59",#"UCAM Murcia",#"58", #"Asefa Estudiantes",#"60", #"Blancos de Rueda Valladolid", #"11", nil];
NSDictionary *posicion = [[NSDictionary alloc] initWithDictionary: [ligaArray objectAtIndex:indexPath.row]];
NSString *equipo = [posicion valueForKey:#"idp"];
NSString *idp = [posicion valueForKey:#"idp"];
NSLog(#"equipo %#", [equiposDic objectForKey:#"49"]);
Many thanks
If posicion is a NSDictionary, you have to use objectForKey in order to get the string.
NSString *idp = [posicion objectForKey:#"idp"];
Is ligaArray available in the context ?
I explain, your ligaArray var maynot be available when you expect it. You have to store ligaArray in your class with a retain
.h :
#interface MyClass {
NSMutableArray *ligaArray;
}
.m :
-viewDidLoad{
[super viewDidLoad];
ligaArray = [[NSMutableArray array] retain];
// OR
ligaArray = [[NSMutableArray alloc] init];
}
-dealloc{
[ligaArray release];
}
If this is not the case, try it. If it's already the case, check the retain/release calls.
This way, you will not loose reference to ligaArray values in your tableview methods.

How to check a class in a NSDictionary

In the below dictionary I want to write a condition for type class, Is there any way to identify the class type alone in the iteraction
NSDictionary *dictionary = [NSDictionary dictionaryWithKeysAndObjects:#"check",#"checkValue",#"webservice", [webservice class], #"list",#"listValue", nil, #"task", [task class], #"new", #"newValue", #"operation",[operation class]];
for(NSString *aKey in dictionary) {
if ([[dictionary valueForKey:aKey]) {
NSLog(#"Getting In");
}
}
Note :I want a single condition to check values [webservice class],[task class],[operation class]
Look up -isKindOfClass: and -isMemberOfClass:
if([object isKindOfClass:[AObjectClass class])
{
NSLog(#"object is of type AObjectClass");
}
See the Apple isKindOfClass: documentation.
- (BOOL)isMemberOfClass:(Class)aClass
this is what u seek probably.
For example, in this code, isMemberOfClass: would return NO:
NSMutableData *myData = [NSMutableData dataWithCapacity:30];
id anArchiver = [[NSArchiver alloc] initForWritingWithMutableData:myData];
if ([anArchiver isMemberOfClass:[NSCoder class]])
//something...
ref: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html
Edit:
In NSDictionaries you will have to put Strings for keys. I suggest you convert the class to a string with NSStringFromClass([MyClass class]); and put that as a key.
The way you want to use the class as a key is impossible.
Found answer for my question in the below link
Check if object is Class type
NSDictionary *dictionary = [NSDictionary dictionaryWithKeysAndObjects:#"check",#"checkValue",#"webservice", [webservice class], #"list",#"listValue", nil, #"task", [task class], #"new", #"newValue", #"operation",[operation class]];
for(NSString *aKey in dictionary) {
if (class_isMetaClass(object_getClass(obj))) {
NSLog(#"Getting In");
}
}

Storing Custom Object in NSMutableDictionary

I am trying to store a custom object in NSMutableDictionary. After saving when I read the object from NSMutableDictionary it's always null.
Here is the code
//Saving
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
CustomObject *obj1 = [[CustomObject alloc] init];
obj1.property1 = #"My First Property";
[dict setObject:obj1 forKey:#"FirstObjectKey"];
[dict writeToFile:[self dataFilePath] atomically:YES];
// Reading
NSString *filePath = [self dataFilePath];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
CustomObject *tempObj = [dict objectForKey:#"FirstObjectKey"];
NSLog(#"Object %#", tempObj);
NSLog(#"property1:%#,,tempObj.property1);
How can I store a custom class object in NSMutableDictionary?
The problem is not with putting the object into the dictionary; the problem is with writing it to a file.
Your custom class has to be serializable. You need to implement the NSCoding protocol so that Cocoa knows what to do with your class when you ask for it to be written out to disk.
This is pretty simple to do; you need to implement two methods that will look something like the following:
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
// If inheriting from a class that implements initWithCoder:
// self = [super initWithCoder:coder];
myFirstIvar = [[coder decodeObjectForKey:#"myFirstIvar] retain];
mySecondIvar = [[coder decodeObjectForKey:#"mySecondIvar] retain];
// etc.
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
// If inheriting from a class that implements encodeWithCoder:
// [super encodeWithCoder:coder];
[coder encodeObject:myFirstIvar forKey:#"myFirstIvar"];
[coder encodeObject:mySecondIvar forKey:#"mySecondIvar"];
// etc.
}
Essentially you're just listing the ivars that you need to save, and then reading them back in properly.
UPDATE: As mentioned by Eimantas, you'll also need NSKeyedArchiver. To save:
NSData * myData = [NSKeyedArchiver archivedDataWithRootObject:myDict];
BOOL result = [myData writeToFile:[self dataFilePath] atomically:YES];
To reload:
NSData * myData = [NSData dataWithContentsOfFile:[self dataFilePath]];
NSDictionary * myDict = [NSKeyedUnarchiver unarchiveObjectWithData:myData];
I think that should do it.
writeToFile method can store only standard types of objects into plist. If you have custom object you'd have to use NSKeyedArchiver/NSKeyedUnarchiver for this.

Adding Objects from an Array into Core Data

So, for the past two days or so I've been struggling with something that should honestly be a simple task. Here's a little introduction on what I'm trying to achieve.
What I'm doing is utilising a web service of my own, sending a request and parsing the returned JSON with SBJSON. What I know want to accomplish with this parsed JSON is to insert it into Core Data.
I have built a object model already which looks like the following:
#import <CoreData/CoreData.h>
#interface Event : NSManagedObject
{
}
#property (nonatomic, retain) NSString * summary;
#property (nonatomic, retain) NSString * content;
#property (nonatomic, retain) NSDate * updated;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSDate * created;
#property (nonatomic, retain) NSString * ID;
#end
These are all built in regards to what is being parsed, I think I may have to change the NSDate's to NSStrings at a later date, but for now they are NSDates.
So, now to show you what is being parsed. The JSON returns the following.
[{"note id":"525","note title":"Car","note summary":"","note content":"","note created":"1297130179","note_updated":"1297233954"},
{"note id":"252","note title":"Premium Users","note summary":"","note content":"","note created":"1296046367","note_updated":"1296699888"},
{"note id":"253","note title":"Welcome!","note summary":"","note content":"","note created":"1296046367","note_updated":"1296561871"}]
What I am wanting to do is create an entity "Event" and each entity stores the respective values for that event. Easy, right? Obviously not for me.
What I have tried...
NotaciousAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newNote;
newNote = [NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
[newNote setValue:[object valueForKey:#"note title"] forKey:#"title"];
[newNote setValue:[object valueForKey:#"note summary"] forKey:#"summary"];
[newNote setValue:[object valueForKey:#"note updated"] forKey:#"updated"];
NSError *error;
[context save:&error];
Yet this returns an error.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "title"; desired type = NSString; given type = __NSArrayI; value = (
Car,
"Premium Users",
"Welcome!"
).'
Any ideas or code samples would help. I really need to get this fixed, all dependent on how this is being stored.
EDIT
Here's how we build the request and parse the string returned.
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:#"api_key"];
[[LRResty client] get:#"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(#"Pulling the users notes \n%#", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
EDIT
Just thought I'd let people know that I'm currently using the Resty RESTful framework to make my calls to my own API. I thought this was the best alternative and easiest way for myself to build a wrapper for it. Here is the full request.
Resty documentation.
-(void)pullNotes {
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:#"api_key"];
[[LRResty client] get:url parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(#"Pulling the users notes \n%#", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
NotaciousAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newNote;
newNote = [NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
[newNote setValue:[object valueForKey:#"note title"] forKey:#"title"];
[newNote setValue:[object valueForKey:#"note summary"] forKey:#"summary"];
[newNote setValue:[object valueForKey:#"note updated"] forKey:#"updated"];
NSError *error;
[context save:&error];
}
if (response.status == 404) {
NSLog(#"FAIL\n%#", [response asString]);
}
}];
}
EDIT
So, now that I have fixed the JSON issue and am grabbing the individual strings and such from each array, I'm having issues storing the parsed strings into Core Data.
I'll show you what I currently have.
[newNote ] is the name given to the Core Data entity in the header file of the following.
-(void)pullNotes {
UIApplication *app = [UIApplication alloc];
app.networkActivityIndicatorVisible = YES;
NSDictionary *params = [NSDictionary dictionaryWithObject:api_key forKey:#"api_key"];
[[LRResty client] get:#"http://notacio.us/api/note" parameters:params withBlock:^(LRRestyResponse *response){
if(response.status == 200) {
NSLog(#"Pulling the users notes \n%#", [response asString]);
// Create SBJSON object to parse JSON
SBJSON *parser = [[SBJSON alloc] init];
// parse the JSON string into an object - assuming [response asString] is a NSString of JSON data
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
NSArray *notes = [object valueForKey:#"result"];
for (NSDictionary *singleNote in notes){
// newNote.created = [singleNote objectForKey:#"note created"]; Need to work on parsing these properly...
// newNote.updated = [singleNote objectForKey:#"note updated"]; Need to work on parsing these properly...
NSString *notetitle = [singleNote objectForKey:#"note title"];
NSString *notesummary = [singleNote objectForKey:#"note summary"];
NSString *noteid = [singleNote objectForKey:#"note id"];
NSString *notecontent = [singleNote objectForKey:#"note content"];
// NSDate *createdDate =
// NSDate *updatedDate =
// If appropriate, configure the new managed object.
[newNote setValue:notetitle forKey:#"title"];
[newNote setValue:notesummary forKey:#"summary"];
[newNote setValue:noteid forKey:#"ID"];
[newNote setValue:notecontent forKey:#"content"];
NSLog(#"value is %#", notetitle);
NSError *error = nil;
if (![newNote.managedObjectContext save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[tableView reloadData];
app.networkActivityIndicatorVisible = NO;
}
}
if (response.status == 404) {
NSLog(#"FAIL\n%#", [response asString]);
app.networkActivityIndicatorVisible = NO;
}
}];
}
#end
However, running this code doesn't actually store the strings into the Core Data entity. As you can see it isn't finalised, a lot of commented code, but the basis is there. ANYWAY, I'm curious as to whether or not it is how I actually implement this in the pulling of the notes itself from the RootViewController...
In viewDidLoad() I'm calling the following...
ntIndex = [IndexNotes alloc];
ntIndex.api_key = api_key;
ntIndex.tableView = self.tableView;
[ntIndex pullNotes];
[ntIndex release];
[self.tableView reloadData];
}
Any help would be great, I'd love to hear what others think the issue is. I don't get any errors with the above code, just nothing is inserted into the Core Data and in turn isn't displayed in my UITableView in RootViewController...
The first thing I would do is log what this line returns:
[object valueForKey:#"note title"]
You'll find it's not the string you're expecting, but is an array of note titles.
eg:
NSLog(#"value is %#", [object valueForKey:#"note title"]);
Then you'll either need to fix your JSON or change the way you parse it.
Edit:
So when I say fix your JSON, I'm no expert, but I think it should look like this:
{"result":[{"note id":"525","note title":"Car","note summary":"","note content":"","note created":"1297130179","note_updated":"1297233954"}, {"note id":"252","note title":"Premium Users","note summary":"","note content":"","note created":"1296046367","note_updated":"1296699888"}, {"note id":"253","note title":"Welcome!","note summary":"","note content":"","note created":"1296046367","note_updated":"1296561871"}]}
Then:
NSDictionary *object = [parser objectWithString:[response asString] error:nil];
NSArray notes = [object valueForKey:#"result"];
for (NSDictionary *singleNote in notes){
[singleNote objectForKey:"note title"] //this gives you the title of the current note your on
}
It's to do with the fact [object valueForKey:#"note title"] is returning an array.
You'll like want to insert something more like [[object valueForKey:#"note title"] objectAtIndex:1] to take an object out of the array. However working out what index you want to insert from the title array is the hardest part.
Tim
EDIT:
Having looked into some others responses its apparent it's returning all the titles in one object. There's something either incredibly funky going on with your JSON. A way around this would be to possibly for loop over your results set from your JSON request and using the index from this loop to insert the correct title.
eg:
int count;
for (count = 0; count < [[object valueForKey:#"note title"] count]; count++)
{
// Do your other insert stuff here
[newNote setValue:[[object valueForKey:#"note title"] objectAtIndex:count] forKey:#"title"];
}
again this is just a dirty example of what you could possibly do so solve this problem.