{"avatar_30":"url",
"id": 81,
"name": "\u674e\u5f3a"
}
I have my Profile Class.
#interface Profile: NSObject{
NSNumber* _identifier;
NSString* _name;
NSString* _avatar_30;
}
#property (nonatomic, retain) NSNumber* identifier;
#property (nonatomic, retain) NSString* name;
#property (nonatomic, retain) NSString* avatar_30;
#end
#implementation Profile
#synthesize identifier=_identifier;
#synthesize name=_name;
#synthesize avatar_30 = _avatar_30;
And then I build my manager and get mapping for Profile Class like these:
in AppDelegate.h
#interface AppDelegate : UIResponder
in AppDelegate.m
RKObjectMapping* profileMapping = [RKObjectMapping mappingForClass:[Profile class]];
[profileMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[profileMapping mapKeyPath:#"name" toAttribute:#"name"];
[profileMapping mapKeyPath:#"avatar_30" toAttribute:#"avatar_30"];
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:_baseURL];
//[manager.mappingProvider setMapping:profileMapping forKeyPath:#"%#"];
[manager loadObjectsAtResourcePath:#"/api/v2/userinfo/100/" objectMapping:profileMapping delegate:self];
I can get the jason correctly, however the profile can not be mapped.
Got a JSON response back from GET!
2012-04-10 17:47:33.480 Base_01[5714:14603] D restkit.network:RKObjectLoader.m:198 Found directly configured object mapping, creating temporary mapping provider for keyPath '%#'
2012-04-10 17:47:33.482 Base_01[5714:fb03] Load collection of Profiles: (
)
Could any have any idea for this problem ? Thanks for your help!
In the method
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects{
are there any objects in the "objects" array?
This should contain all your Profile objects.
Related
I have an 2 objective C classes:
Class 1 scanDatabase (Scans a database and puts that into a mutable array)
Class 2 Mapper (Is a mapping class for the database scan "model")
In objective C this successfully scans the database and puts it into a mutable array. Using the mapping class I can access individual groups of elements (AlbumTitles) like so:
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
Everything is working as it should and I can return individual elements from my array i.e as above I am only returning album titles.
I then need to use that array in Swift. I call the objective C in my Swift class and again it runs fine and creates the array. This is done with:
let scanTable = ScanTable();
let scanMapper = Mapper();
scanTable.scanTableDo();
but when I try to retrieve a particular set of items like Album title as I did in the objective C for loop above I get the error "scanMapper is not a type" (scanMapper is my swift instance of the objective C mapper class:
I tried two different ways and both have the same error:
for mapper: scanMapper in scanTable.scanResult {
print("\(mapper.AlbumTitle)")
}
for object in scanTable.scanResult as! [scanMapper] {
print("\(mapper.AlbumTitle)")
}
Can I use an objective C class as a model/mapper and not sure whether I would need to recreate it in Swift.
I will include the mapper and scanTable .h and .m code just in case it is needed, plus the bridging header:
Mapper.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface Mapper : AWSDynamoDBObjectModel <AWSDynamoDBModeling>
#property (nonatomic, strong) NSNumber *SongID;
#property (nonatomic, strong) NSString *Artist;
#property (nonatomic, strong) NSString *SongURL;
#property (nonatomic, strong) NSString *Location;
#property (nonatomic, strong) NSNumber *UserRatings;
#property (nonatomic, strong) NSNumber *AVGUserRating;
#property (nonatomic, strong) NSString *Category;
#property (nonatomic, strong) NSString *PictureURL;
#property (nonatomic, strong) NSNumber *SongDuration;
#property (nonatomic, strong) NSString *SongTitle;
#property (nonatomic, strong) NSNumber *AVGMusicianRating;
#property (nonatomic, strong) NSString *AlbumTitle;
#end
Mapper.m
#import <AWSDynamoDB/AWSDynamoDB.h>
#import "Mapper.h"
#implementation Mapper
+ (NSString *)dynamoDBTableName {
return #"Songs";
}
+ (NSString *)hashKeyAttribute {
return #"SongID";
}
#end
ScanTable.h:
#import <Foundation/Foundation.h>
#import <AWSDynamoDB/AWSDynamoDB.h>
#interface ScanTable : NSObject
- (void) scanTableDo;
#property (nonatomic, strong) NSMutableArray *scanResult;
#end
ScanTable.m
#import "ScanTable.h"
#import "Mapper.h"
#implementation ScanTable
- (void) scanTableDo {
AWSDynamoDBObjectMapper *dynamoDBObjectMapper = [AWSDynamoDBObjectMapper defaultDynamoDBObjectMapper];
AWSDynamoDBScanExpression *scanExpression = [AWSDynamoDBScanExpression new];
scanExpression.limit = #10;
[[dynamoDBObjectMapper scan:[Mapper class]
expression:scanExpression]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
NSMutableArray *scanResult = [[NSMutableArray alloc] initWithArray:paginatedOutput.items]; //// ADDED /////
for (Mapper *mapper in scanResult) {
NSLog(#"%#", mapper.AlbumTitle);
}
}
return nil;
}];
}
#end
//EDITED ADDED BRIDGING HEADER//
MySampleApp-Bridging-Header.h:
//
// MySampleApp-Bridging-Header.h
// MySampleApp
#import "ScanTable.h"
#import "Mapper.h"
#import "Hello World.h"
Thanks for your help
The problem is just as the error explains, you're attempting to cast the items in your array to scanMapper, which is a variable holding an instance of Mapper, not the Mapper type itself. Assuming that scanTable.scanResult is an NSArray of Mappers, try this instead:
guard let scanResult = scanTable.scanResult as? [Mapper] else {
print("scanResult was not an array of mappers!")
return
}
for mapper: Mapper in scanResult {
print("\(mapper.AlbumTitle)")
}
So after reading this answer, I do not see how my case is any different, but the proposed solution just doesn't seem to work in RestKit 0.20.3
For the following json:
{
"photos": [
"872ac577-3f31-47a0-966e-6f2ed2fbabcd"
],
"imageUrl": [
"http://domain/WebAPI/FileUpload/10232013629_21382571406.439790.jpeg"
]
}
Mapping into the following object:
#class Picture, Site, Survey;
#interface Job : NSManagedObject
#property (nonatomic, retain) NSString * jobId;
#property (nonatomic, retain) NSString * jobName;
#property (nonatomic, retain) NSString * jobStatusId;
#property (nonatomic, retain) NSString * jobStatus;
#property (nonatomic, retain) NSString * projectId;
#property (nonatomic, retain) NSString * projectType;
#property (nonatomic, retain) NSString * siteId;
#property (nonatomic, retain) NSSet *surveys;
#property (nonatomic, retain) NSSet *photos;
#property (nonatomic, retain) NSDate *createdDate;
#property (nonatomic, retain) Site *site;
#end
Where photos is a Picture object that looks like this:
#interface Picture : NSManagedObject
#property (nonatomic, retain) NSData * data;
#property (nonatomic, retain) NSString * photoId;
#property (nonatomic, retain) NSString * pictureLoc;
#property (nonatomic, retain) NSString * imageUrl;
#property (nonatomic, retain) Job *job;
#property (nonatomic, retain) QuestionAnswer *question;
#end
I want to map the photos and imageUrl into the Picture's photoId and imageUrl, respectively. I thought I could use the following in my Job's mapping:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[ret addAttributeMappingsFromDictionary: #{
#"photos": #"photos.photoId",
#"imageUrl": #"photos.imageUrl",
}];
However, the mapped Job always ends up with nothing in the photos property.
I know that I am doing all the in-between steps correctly (like actually using ret for the mapping), as the JSON is a snippet that contains many other keys I am able to map with no problem. What am I doing wrong? please tell me
EDIT
Changed the mapping code as per Wain's answer:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
RKEntityMapping *subMapping = [RKEntityMapping mappingForEntityForName:#"Picture" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[subMapping addAttributeMappingsFromDictionary: #{
#"photos":#"photoId",
#"imageUrl":#"imageUrl"
}];
[ret addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:nil
toKeyPath:#"photos"
withMapping:subMapping]];
And now I get a Picture object added, but with nil for photoId and imageUrl (and yes, I checked to make sure I have the spelling and capitalization correct for the JSON keys)
The reason photos never has anything in it is that your mapping doesn't tell it to create a new object.
The problem is how you could tell it to create a single object and set both of the parameters into it. The problem is because you have 2 different arrays. The JSON I would expect you to have is 1 array which contains dictionaries with all of the details for that particular entry. RestKit can process both of the arrays but it would result in 2 distinct photo objects being created. To do this you need a second mapping with a nil key path and a relationship from ret to that new mapping.
An alternative could be to change the Job object so that it has 2 transformable (or transient) properties which are NSArrays. If you set ret to just map #"photos" : #"photos" and #"imageUrl" : #"imageUrl" then RestKit will map all of the data, just not into a photo object. You can then mutate this data after the mapping has completed if you want to...
I'm seeing some strange behavior with RestKit and mapping an object. In my destination object, if I have an NSString * property named 'description', then the entire mapping fails. Changing this to any other name, or commenting it out, everything succeeds. This happens regardless if I have a map set up for the property, and I can reproduce it in simple test classes.
The JSON the server is returning:
{
id = 1;
name = item1;
},
{
id = 2;
name = item2;
}
The object I want to map to, SimpleObject.h. Note that neither the description, nor the nonexistant property is in the JSON, meaning I don't think it's because 'description' is missing:
#import <Foundation/Foundation.h>
#interface SimpleObject : NSObject
#property (nonatomic, assign) NSInteger identifier;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSString *nonexistant;
#end
SimpleObject.m:
#import "SimpleObject.h"
#implementation SimpleObject
#synthesize identifier;
#synthesize name;
#synthesize description;
#synthesize nonexistant;
#end
I create the mapping in my AppDelegate:
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURLString:#"http://127.0.0.1/test"];
// Setup our object mappings
RKObjectMapping *testMapping = [RKObjectMapping mappingForClass:[SimpleObject class]];
[testMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[testMapping mapKeyPath:#"name" toAttribute:#"name"];
// Register our mappings with the provider using a resource path pattern
[objectManager.mappingProvider setObjectMapping:testMapping forResourcePathPattern:#"/products"];
Inside my didLoadObjects method, I have:
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects
{
NSLog(#"Loaded products: %#", objects);
SimpleObject *obj = [objects objectAtIndex:0];
NSLog(#"Loaded product ID %d -> Name: %# ", obj.identifier, obj.name);
}
This outputs:
Finished performing object mapping. Results: {
"" = (
(null),
(null)
);
}
If I comment out the description property (but leave in nonexistant), then everything works:
Finished performing object mapping. Results: {
"" = (
"<SimpleObject: 0x8a4d040>",
"<SimpleObject: 0x8a50130>"
);
}
Is there a reason the mapping is failing only for this property name? If I change the name to anything other than 'description', it also succeeds.
It is conflicting with description property of NSObject. Rename it to other(like desc).
With the help of mja, I managed to successfully set up simple object mapping using RestKit and Objective-C. Please see my previous question here.
My next step was to attempt to deal with nested JSON in the same way.
My JSON looks like this, with the outer being a CandidatePhrase with some nested 'Votes':
{ "Id":33696,
"Phrase": "phrase",
"BadCount":0,
"Votes":[{"Id":447,"OriginalId":33696,"Votes":2,"Translation":"translation 1"},
{"Id":746,"OriginalId":33696,"Votes":1,"Translation":"translation 2"},
{"Id":747,"OriginalId":33696,"Votes":1,"Translation":"translation 3"}
]}
I created a relationship in my AppDelegate as follows:
[candidatePhraseMapping mapKeyPath:#"votes" toRelationship:#"vote" withMapping:voteMapping];
When I call make my request in my controller, I'm able to deal with the rest of the CandidatePhrase okay, but am not really sure how to map the nested 'Vote' objects into an array so I can use them in a TableView
(pseudo-code something like this...)
// Store the votes in an array
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
Here's my CandidatePhrase Object
#interface CandidatePhrase : NSObject
#property (nonatomic, retain) NSNumber* ident;
#property (nonatomic, retain) NSNumber* badcount;
#property (nonatomic, retain) NSString* phrase;
#property (nonatomic, retain) NSArray* votes;
#end
and my Vote object
#interface Vote : NSObject
#property (nonatomic, retain) NSNumber* ident;
#property (nonatomic, retain) NSNumber* originalId;
#property (nonatomic, retain) NSNumber* votecount;
#property (nonatomic, retain) NSString* translation;
+ (id)voteWithTranslationId:(NSNumber *)ident translation:(NSString *)translation;
#end
Any help would be much appreciated.
EDIT
Below is my mapping code
// Votes Mapping
RKObjectMapping* voteMapping = [RKObjectMapping mappingForClass:[Vote class]];
[voteMapping mapKeyPath:#"Id" toAttribute:#"ident"];
[voteMapping mapKeyPath:#"OriginalId" toAttribute:#"originalId"];
[voteMapping mapKeyPath:#"Votes" toAttribute:#"votecount"];
[voteMapping mapKeyPath:#"Translation" toAttribute:#"translation"];
[[manager mappingProvider] addObjectMapping:voteMapping];
// Candidate Phrase Mapping
RKObjectMapping *candidatePhraseMapping = [RKObjectMapping mappingForClass:[CandidatePhrase class]];
[candidatePhraseMapping mapKeyPath:#"Id" toAttribute:#"ident"];
[candidatePhraseMapping mapKeyPath:#"Phrase" toAttribute:#"phrase"];
[candidatePhraseMapping mapKeyPath:#"BadCount" toAttribute:#"badcount"];
[candidatePhraseMapping mapKeyPath:#"Votes" toRelationship:#"votes" withMapping:voteMapping];
[[manager mappingProvider] addObjectMapping:candidatePhraseMapping];
For clarity also, here's how I'm attempting to access the vote items on the controller
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
self.candidateText.text = myCandidatePhrase.phrase; <-- works fine
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
for (id o2 in _votes) {
//Vote *vote = o2;
NSLog(#"Item name: %#", o2); <-- sees object but crashes
}
NSLog(#"Votes: %#", myCandidatePhrase.votes);
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
[_votesTableView reloadData];
}
and my table is binding with
Vote *vote = [_votes objectAtIndex:indexPath.row];
cell.textLabel.text = vote.translation;
You do not need to manually manage the nested NSArray. I believe the problem might be just a simple typo, as you map keyPath "votes", but your json contain "Votes" with capital "V".
[candidatePhraseMapping mapKeyPath:#"Votes" toRelationship:#"votes" withMapping:voteMapping];
If this doesn't help feel free to leave a comment and update your question with voteMapping.
Also, the contents of the didLoadObject can be simplified:
//in .h file
#property (nonatomic, retain) NSArray* votes;
// in implementation
#synthesize votes;
...
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
self.candidateText.text = myCandidatePhrase.phrase;
self.votes = myCandidatePhrase.votes;
}
I would like to access the the _news items in a loop. But don't know how to get this done.
My Game.m looks like
#import "Game.h"
#implementation Game
#synthesize homename = _homename;
#synthesize guestname = _guestname;
#synthesize date = _date;
#synthesize gametype = _gametype;
#synthesize news = _news;
#synthesize gameId = _gameId;
-(NSString*)description{
return [NSString stringWithFormat:#"%# gegen %# (%#)", self.homename, self.guestname, self.gametype];
}
#end
My Game.h looks like
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#import "News.h"
#interface Game : NSObject {
NSString* _homename;
NSString* _guestname;
NSString* _date;
NSString* _gametype;
NSNumber* _gameId;
// News* news;
#public
NSArray* _news;
}
#property (nonatomic, retain) NSString* homename;
#property (nonatomic, retain) NSString* guestname;
#property (nonatomic, retain) NSString* date;
#property (nonatomic, retain) NSString* gametype;
//#property (nonatomic, retain) News* news;
#property (nonatomic, retain) NSArray* news;
#property (nonatomic, retain) NSNumber* gameId;
#end
My News.h looks like
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#interface News : NSObject {
NSString* _minute;
NSString* _title;
NSString* _bodytext;
NSString* _player;
}
#property (nonatomic, retain) NSString* minute;
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) NSString* bodytext;
#property (nonatomic, retain) NSString* player;
#end
My news.m looks like
#import "News.h"
#implementation News
#synthesize player = _player;
#synthesize title = _title;
#synthesize bodytext = _bodytext;
#synthesize minute = _minute;
#end
And the code where I want to access the variable looks like:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
HeaderText.text = [NSString stringWithFormat:#"%#", objects ];
// for(News* n in _news) NSLog([n _bodytext]);
// for (id object in objects) {
// NSLog(#"News = %#", object );
// }
}
The NSLog with objects looks good for the game. The next thing is that I want to do something like (above is more pseudo code than real code because I don't know how to do it right):
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
HeaderText.text = [NSString stringWithFormat:#"%#", objects ];
// loop all
for (id object in objects) {
news = objects.news;
for (id mynews in news) {
NSLog(#"News minute = %#", news.minute );
NSLog(#"News title = %#", news.title );
NSLog(#"News bodytext = %#", news.bodytext );
NSLog(#"News player = %#", news.player );
}
}
}
How to do the getter/setter methods right (so that I can use it here)?
Sorry for the surely stupid question but I don't get the point with it.
Two things:
1stly, is the _news object a private variable, without having a property declaration (getters and setters for e.g.)? The '_variableName' format is usually used to denote private variables.
2ndly, if it is not a private variable, do all the items within the _news array belong to the same class?
If so, you can do a
for (NewsObject *theNewsObject in _news)
{
//code here
}
The for(id randomObject in array) is useful when you don't know what type of object is in the array or if the objects contained in the array are of different types.
Now, again, all the objects inside the NewsObject ought to be public properties that can be accessed by other classes (they should have getters and setters).
Hope this helps. :)
EDIT FOR UPDATED QUESTION
So, if I'm getting your question correctly, you have a Game object, which has an array of News object inside it.
So, in your Game.h
NSString* _homename;
NSString* _guestname;
NSString* _date;
NSString* _gametype;
NSNumber* _gameId;
NSArray * _newsObjects; //declare it as NSMutableArray if you need to mutate it
Now, where you declare your properties;
#property(nonatomic, retain)NSArray *newsObjects
Synthesize it like you normally would in the Game.m file
You are creating the getters/setters automatically by using the #synthesize directive. It creates the getters and setters for you.
Again, from your code, it looks like the NSArray of objects that are passed through the method
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects
consist of Game objects.
So, to access the News object from within the array of Game objects, import News.h and Game.h in the class where this method is being executed, and do the following:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
// loop all
for (Game *gameObject in objects) {
NSArray *newsObjectArray = [gameObject newsObjects] //or gameObject.newsObject
for (News *mynews in newsObjectArray) {
NSLog(#"News minute = %#", mynews.minute );
NSLog(#"News title = %#", mynews.title );
NSLog(#"News bodytext = %#", mynews.bodytext );
NSLog(#"News player = %#", mynews.player );
}
}
}
What I found in your code that there was code that was being executed which was not declared in any part of the example that you posted.
What the code above will do is it will look through the Game object array (called objects).
Then, for each gameObject within that array, it will loop through the array of newsObject for that gameObject.
Hope this helps. :)