NSFetchRequest doesn't give back anything - objective-c

I am trying to get all the pictures of an album. I am getting this by an webservice. The webservice has the following layout.
{
"name": "Club Brugge - KRC Genk",
"date": "08.10.2012",
"albumId: 1,
"pictures": [
{
"pic_album_id"=1,
"pic_id" = 1,
"url": "http://www.krcgenk.be/images/gallery/album_199/800X600/1a06dc0e405fd0219e3d327f1eec7fbf.jpg"
},
{
"pic_album_id"=1,
"pic_id" = 2,
"url": "http://www.krcgenk.be/images/gallery/album_199/800X600/e8e10c0664eb0533a0534ed69891b165.jpg"
},
{
"pic_album_id"=1,
"pic_id"= 3,
"url": "http://www.krcgenk.be/images/gallery/album_199/800X600/750b55a87b8eae33b8f3278add9bec44.jpg"
}
]
I have the following functions to get all pictures of a certain album.
- (NSMutableArray *)getAllPicturesOfAlbumId: (int)AlbumId
{
NSString *picture_Url = [[NSString alloc]init];
NSArray *results = [[NSArray alloc]init];
_picturesForAlbum = [[NSMutableArray alloc]init];
picture_Url = #"";
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"pic_album_id == %#",
[NSNumber numberWithInt:AlbumId]];
NSLog(#"album id: %#",[NSNumber numberWithInt:AlbumId]);
[request setEntity:[NSEntityDescription entityForName:#"Picture" inManagedObjectContext:self.genkDatabase.managedObjectContext]];
[request setPredicate:predicate];
NSError *error = nil;
results = [self.genkDatabase.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"results: %#",results);
if (results == nil) {
NSLog(#"nil results");
// handle errors
} else if (results.count == 0) {
NSLog(#"0 results");
// nothing found
} else {
for(int i = 0; i < results.count ; i++){
Picture *picture = [results objectAtIndex:i];
NSLog(#"%#",[results objectAtIndex:i]);
[_picturesForAlbum addObject:picture];
}
}
NSLog(#"%#",_picturesForAlbum);
//NSLog(#"album: %#",[_picturesForAlbum objectAtIndex:5]);
return _picturesForAlbum;
}
The code above, gives the following log. (I gave for testcase AlbumId = 3 with it)
2012-10-12 14:53:04.577 RacingGenk[4793:c07] album id: 3
2012-10-12 14:53:04.578 RacingGenk[4793:c07] results: (
)
2012-10-12 14:53:04.578 RacingGenk[4793:c07] (
)
Hope anybody can help me.
Kind regards.
** Here you see a screenshot of my database model
EDIT
Here you see how I get my pictures and put it in my database.
+ (Picture *)pictureWithGenkInfo:(NSDictionary *)genkInfo
inManagedObjectContext:(NSManagedObjectContext *)context
withAlbumId:(int)albumId
withPictureId:(int)pictureId;
{
Picture *picture = nil;
picture = [NSEntityDescription insertNewObjectForEntityForName:#"Picture"
inManagedObjectContext:context];
picture.url = [genkInfo objectForKey:PICTURES_URL];
picture.pic_album_id = [NSNumber numberWithInt:albumId];
picture.picture_id = [NSNumber numberWithInt:pictureId];
return picture;
}
//The method above is called in my first controller that comes one screen. With this loop I fill up my core data-base.
for (NSDictionary *genkInfo in albums ) {
albumId++;
[Album albumWithGenkInfo:genkInfo inManagedObjectContext:document.managedObjectContext withAlbumId:albumId];
for (NSDictionary *genkInfo2 in pictures ) {
pictureId++;
[Picture pictureWithGenkInfo:genkInfo2 inManagedObjectContext:document.managedObjectContext withAlbumId:albumId withPictureId:pictureId];
}
pictureId = 0;
// table will automatically update due to NSFetchedResultsController's observing of the NSMOC
}

Use predicate
[NSPredicate predicateWithFormat:#"whichAlbum.album_id == %d", AlbumId];
UPDATE
for (NSDictionary *genkInfo in albums ) {
albumId++;
Album *album = [Album albumWithGenkInfo:genkInfo inManagedObjectContext:document.managedObjectContext withAlbumId:albumId];
for (NSDictionary *genkInfo2 in pictures ) {
pictureId++;
Picture *pic = [Picture pictureWithGenkInfo:genkInfo2 inManagedObjectContext:document.managedObjectContext withAlbumId:albumId withPictureId:pictureId];
[album addPictureObject:pic]; // this method should be automatically generated
}
pictureId = 0;
// table will automatically update due to NSFetchedResultsController's observing of the NSMOC
}
// don't forget save context

How to troubleshoot:
Get yourself Liya or another sqlite database viewer. Navigate to the folder with the core data database. On the simulator that would be
/Users/<username>/Library/Application Support/iPhone Simulator/<version of iOS>/Applications/<long cryptig string>/<folder-with-the-core-data-database>
Open the sqlite database, navigate to the ZPicture table and check that data is present.
If there is no data then the the webservice -> core-data program logic has a problem.
If there is data and it appears to be good go back to Xcode and enable logging for the core-data request. Edit the scheme (Product - Edit scheme), select the Run, Debug scheme and change the tab to Arguments. In the field Arguments Passed On Launch add -com.apple.CoreData.SQLDebug 3. Ensure the check box is ticked.
Run your app and in the debugger console window there should be the sql code generated by core-data.
You will see something similar to: (you might see the whichAlbum showing up)
<timestamp> CoreData: annotation: fetch using NSSQLiteStatement <0xfxxxxxx> on entity
'Picture' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.zpic_album_id
FROM zpicture t0 WHERE t0.zpic_album_id == ?'
<timestamp> CoreData: details: SQLite bind[0] = (int64)3
Take the sql command, return to Liya and paste the command into the Run Custom Command field. Replace the question mark in
t0.zpic_album_id == ?
with the album_id you want to see.
The result of the query will be what Core Data will retrieve.
Not sure where the whichAlbum would go. Just come back with your finding and let us know.

Related

PHPhotoLibrary getting album and photo info

I am trying to get info on all the albums/photos using the PHPhotoLibrary. I barely know objective C, and i've looked at some tutorial/sample but couldn't find everything that I needed.
Here is a link to the sample code I based my code on.
https://developer.apple.com/library/ios/samplecode/UsingPhotosFramework/Introduction/Intro.html#//apple_ref/doc/uid/TP40014575-Intro-DontLinkElementID_2
So far I was able to get the albums name and identifier. And I am getting a list of photos, I am able to get their identifier as well, but not the filename. But if I put a break point in my fonction and look at my PHAsset pointer values, I can see the filename there (inside _filename), but if I try to call the variable with the filename in it, the variable does not exist.
So if anyone can provide a sample code to get all info on albums/photos/thumbnail that would be awesome. Or just getting the filename would be a good help.
Here is the code I have tried so far:
-(void)awakeFromNib{
NSMutableArray *allPhotos = self.getAllPhotos;
for (int x = 0; x < allPhotos.count; x ++)
{
PHAsset *photo = [self getPhotoAtIndex:x];
PHAssetSourceType source = photo.sourceType;
NSString *id = photo.localIdentifier;
NSString *description = photo.description;
NSUInteger height = photo.pixelHeight;
NSUInteger width = photo.pixelWidth;
NSLog(#"Test photo info");
}
}
-(PHAsset*) getPhotoAtIndex:(NSInteger) index
{
return [self.getAllPhotos objectAtIndex:index];
}
-(NSMutableArray *) getAllPhotos
{
NSMutableArray *photos = [[NSMutableArray alloc] init];
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
PHFetchResult *fetchResult = #[allPhotos][0];
for (int x = 0; x < fetchResult.count; x ++) {
PHAsset *asset = fetchResult[x];
photos[x] = asset;
}
return photos;
}
As you can see, I can get the image height and width, its id, but cannot get the url to it.
I have found a way to get the url of my photo.
-(void)getImageURL:(PHAsset*) asset
{
PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
[options setCanHandleAdjustmentData:^BOOL(PHAdjustmentData *adjustmentData) {
return [adjustmentData.formatIdentifier isEqualToString:AdjustmentFormatIdentifier] && [adjustmentData.formatVersion isEqualToString:#"1.0"];
}];
[asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info)
{
NSURL* url = contentEditingInput.fullSizeImageURL;
}];
}
Filenames in the Photos library are an implementation detail and subject to change. There are various private API for discovering them (or ways to use valueForKey or other public introspection APIs to find where they're hidden), they aren't something to be relied upon. In particular, an asset that's been edited is likely to have a different filename than the original.
What do you need a filename/URL for? If you're just uniquely identifying the asset across launches of your app, use localIdentifier. If you're showing it to the user... why? Something like IMG_0234.jpg vs IMG_5672.jpg has little meaning to the average user.
To fetch the assets in a specific album, use fetchAssetsInAssetCollection:options:. To fetch the album(s) containing a specific asset, use fetchAssetCollectionsContainingAsset:withType:options:. To discover the list(s) of albums, use other APIs on PHAssetCollection and its superclass PHCollection.

Error saving core data object

I am quite confused about one error I am receiving when saving an object. I am receiving the following error (when I print out the detailed description):
2015-05-08 08:19:51.589 Br[11240:208443] Core Data Save Error
NSValidationErrorKey
activityObject
NSValidationErrorPredicate
(null)
NSValidationErrorObject
<BRActivity: 0x7deb2aa0> (entity: BRActivity; id: 0x7deb2780 <x-coredata:///BRActivity/t86C0E8CD-2B6C-4AF7-986A-4797B7BEFDF5> ; data: {
activities = (
);
activityObject = nil;
activityType = 0;
body = "Someone left a comment on your post.";
timestamp = "2015-05-08 04:24:46 +0000";
uuidString = "c6a06b45-e2d5-45bd-9f64-20b13ac87526";
})
NSLocalizedDescription
The operation couldn’t be completed. (Cocoa error 1570.)
2015-05-08 08:19:51.590 Br[11240:208443] Core Data Save Error
So from looking on the internet, it seems to be a relationship problem. So a Br post has a relationship called activities relationship, the inverse of which is an activity object. Now that, as we can see from the error, is nil. So what kind of solution are we looking at here... Is there a way to make the relationship "optional" (ok to be nil!) or should I add an activity object? I really don't want to break anything here so if there's a subtle solution let me know. Thanks a bunch guys!
Also here is the method surrounding the save:
- (void)importArray:(NSArray *)array entityName:(NSString *)entityName attributeName:attributeName error:(NSError *__autoreleasing *)error {
NSParameterAssert(array);
NSParameterAssert(entityName);
[self.context performBlockAndWait:^{
for (NSDictionary *jsonDictionary in array) {
NSManagedObject *managedObject = [NSManagedObject upsertWithContext:self.context entityName:entityName dictionary:jsonDictionary attributeName:attributeName error:error];
if (nil == managedObject) {
if ([self.context hasChanges]) {
[self.context rollback];
}
return;
}
}
if ([self.context hasChanges]) {
if (![self.context save:error]) {
NSError *err = *error;
NSDictionary *userInfo = [err userInfo];
if ([userInfo valueForKey:#"NSDetailedErrors"] != nil) {
// ...and loop through the array, if so.
NSArray *errors = [userInfo valueForKey:#"NSDetailedErrors"];
for (NSError *anError in errors) {
NSDictionary *subUserInfo = [anError userInfo];
subUserInfo = [anError userInfo];
// Granted, this indents the NSValidation keys rather a lot
// ...but it's a small loss to keep the code more readable.
NSLog(#"Core Data Save Error\n\n \
NSValidationErrorKey\n%#\n\n \
NSValidationErrorPredicate\n%#\n\n \
NSValidationErrorObject\n%#\n\n \
NSLocalizedDescription\n%#",
[subUserInfo valueForKey:#"NSValidationErrorKey"],
[subUserInfo valueForKey:#"NSValidationErrorPredicate"],
[subUserInfo valueForKey:#"NSValidationErrorObject"],
[subUserInfo valueForKey:#"NSLocalizedDescription"]);
}
}
NSLog(#"Error: %#", err.localizedDescription);
}
return;
}
}];
}
Yes, relationships can be optional. Select the relationship and you will see the optional optional in the Data inspector pane on the top right.

A solution for my issue with sending some info from an array to Parse

I am making an e-commerce app and trying to deal with the backend stuff so the shop owner can see what a user ordered. I am going to use Stripe + Parse for this.
In my Payment View Controller I use fast enumeration to go through my array and get the items in the shopping cart. The items in my array can only be one of two custom objects (Bike or Accessory).
Then I put the objects I get back into a NSDictionary, which is required to use my Stripe token and Parse together. Then the items in my NSDictionary should come out listed in my Parse log.
My issue is that this only works if I have one Bike and/or one Accessory. If I add a second Bike or Accessory to my cart, it just replaces the old info with my new Bike or Accessory.
I can kinda see in my code why this happens but I don't really know how to find a solution (still a newbie to programming).
Would appreciate your help! Code below:
Cart *cartObject = [Cart sharedManager];
for (id object in cartObject.cartArray)
{
if ([object isKindOfClass:[ChosenBike class]])
{
ChosenBike *bikeObject = (ChosenBike *)object;
self.bikeName = bikeObject.chosenName;
self.bikeSize = bikeObject.chosenSize;
self.bicycleHasRearBrake = bikeObject.bicycleHasRearBrake;
self.bikeWheelSetColor = bikeObject.chosenWheelSetColor;
self.bikeExtraWheelset = bikeObject.extraSeriesWheelset;
self.bikeQty = bikeObject.chosenQuantity;
}
else if ([object isKindOfClass:[ChosenAccessory class]])
{
ChosenAccessory *accessoryObject = (ChosenAccessory *)object;
self.accessoryName = accessoryObject.chosenName;
self.accessoryQty = accessoryObject.chosenQuantity;
self.accessoryColor = accessoryObject.color;
self.accessorySize = accessoryObject.chosenSize;
}
}
NSDictionary *chargeParams = #{
#"token": token.tokenId,
#"currency": #"usd",
#"amount": result, // this is in cents (i.e. 1000 = $10)
#"bikeName": self.bikeName,
#"bikeSize": self.bikeSize,
#"bikeHasRearBrake": [NSNumber numberWithBool:self.bicycleHasRearBrake],
#"bikeColor": self.bikeWheelSetColor,
#"bikeExtraWheelset": self.bikeExtraWheelset,
#"bikeQty": [self.bikeQty stringValue],
#"accessoryName": self.accessoryName,
#"accessoryQty": [self.accessoryQty stringValue],
#"accessoryColor": self.accessoryColor,
#"accessorySize": self.accessorySize,
};
// This passes the token off to our payment backend, which will then actually complete charging the card using your account's
[PFCloud callFunctionInBackground:#"charge"
withParameters:chargeParams
block:^(id object, NSError *error) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
if (error) {
[self hasError:error];
return;
}
[self.presentingViewController dismissViewControllerAnimated:YES
completion:^{
[[[UIAlertView alloc] initWithTitle:#"Payment Succeeded"
message:nil
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] show];
}];
}];
}
If you need to support more than one instance of each type of product, then you should probably restructure your charge params dictionary to support arrays. It's common in commerce systems to have the concept of a "line items", which would be a set of items in the order. For each of the line items, you could have a line_item_type, and a dictionary of type_attributes which would differ based on the line_item_type. This way, you can capture the specific attributes of your accessories and bikes, but in a generic line_item structure.
A structure that resembles something like below should work for what you're after. This example has 2 bike items and 1 accessory.
{ "token": token.tokenId,
"currency": "usd",
"amount": result,
"line_items": [
{ "line_item_type": "bike",
"line_item_attributes": {
"bikeName": self.bikeName,
"bikeSize": self.bikeSize,
"bikeHasRearBrake": [NSNumber numberWithBool:self.bicycleHasRearBrake],
"bikeColor": self.bikeWheelSetColor,
"bikeExtraWheelset": self.bikeExtraWheelset,
"bikeQty": [self.bikeQty stringValue]
}
},
{ "line_item_type": "bike",
"line_item_attributes": {
"bikeName": self.bikeName,
"bikeSize": self.bikeSize,
"bikeHasRearBrake": [NSNumber numberWithBool:self.bicycleHasRearBrake],
"bikeColor": self.bikeWheelSetColor,
"bikeExtraWheelset": self.bikeExtraWheelset,
"bikeQty": [self.bikeQty stringValue]
}
},
{ "line_item_type": "accessory",
"line_item_attributes": {
"accessoryName": self.accessoryName,
"accessoryQty": [self.accessoryQty stringValue],
"accessoryColor": self.accessoryColor,
"accessorySize": self.accessorySize
}
}]
}
--- Update with example of adding a dictionary to an array ---
NSMutableArray *lineItems = [NSMutableArray new];
Cart *cartObject = [Cart sharedManager];
for (id object in cartObject.cartArray) {
if ([object isKindOfClass:[ChosenBike class]]) {
ChosenBike *bikeObject = (ChosenBike *)object;
NSDictionary *attributes = #{#"bikeName": bikeObject.chosenName,
#"bikeSize": bikeObject.chosenSize,
#"bikeHasRearBrake": #(bikeObject.bicycleHasRearBrake),
#"bikeColor": bikeObject.chosenWheelSetColor,
#"bikeExtraWheelset": bikeObject.extraSeriesWheelset,
#"bikeQty": [bikeObject.extraSeriesWheelset stringValue]};
NSDictionary *lineItem = #{#"line_item_type": #"bike",
#"line_item_attributes": attributes};
[lineItems addObject:lineItem];
} else if ([object isKindOfClass:[ChosenAccessory class]]) {
ChosenAccessory *accessoryObject = (ChosenAccessory *)object;
NSDictionary *attributes = #{#"accessoryName": accessoryObject.chosenName,
#"accessoryQty": [accessoryObject.chosenQuantity stringValue],
#"accessoryColor": accessoryObject.color,
#"accessorySize": accessoryObject.chosenSize};
NSDictionary *lineItem = #{#"line_item_type": #"accessory",
#"line_item_attributes": attributes};
[lineItems addObject:lineItem];
}
}
NSDictionary *chargeParams = #{#"token": token.tokenId,
#"currency": #"usd",
#"amount": result,
#"line_items": lineItems};

I don't understand how to use the JSON data in Xcode

I have successfully downloaded and parsed (I think) the JSON data
NSURL *quoteURL = [NSURL URLWithString:#"http://www.qwoatzz.com"];
NSData *jsonData = [NSData dataWithContentsOfURL:quoteURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
However, I'm not too sure how to actually use the JSON data. If the JSON file has an array with two keys, how do I get a value from one of these keys at a specific index (the first one for example) and use that to change the text of a label (I know how to do that, it's just the JSON part I am stuck on)?
2014-10-20 19:46:10.616 Qwoatz-2[3147:454481] dataDictionary : {
count = 10;
"count_total" = 1871;
pages = 188;
posts = (
{
author = "Jason Seifer";
date = "2014-10-20 13:54:11";
id = 24317;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/1634685862_92b26b9167_o-150x150.jpg";
title = "What Employers Are Looking For in a Junior Rails Developer";
url = "http://blog.teamtreehouse.com/employers-looking-junior-rails-developer";
},
{
author = "Zac Gordon";
date = "2014-10-16 09:27:38";
id = 24296;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/brochure-site-150x150.png";
title = "When is WordPress.com the Right Solution?";
url = "http://blog.teamtreehouse.com/wordpress-com-right-solution-website";
},
{
author = "Gill Carson";
date = "2014-10-15 12:52:43";
id = 24287;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/Tahoe_team-Photo-150x150.jpg";
title = "We Are Family – The Whole Team!";
url = "http://blog.teamtreehouse.com/family";
},
{
author = "Jason Seifer";
date = "2014-10-14 15:26:11";
id = 24292;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/Chartist-Simple-responsive-charts-2014-10-14-15-24-43-150x150.jpg";
title = "Responsive Charts";
url = "http://blog.teamtreehouse.com/responsive-charts";
},
{
author = "Guil Hernandez";
date = "2014-10-13 09:28:05";
id = 24228;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/09/blend-mode-mult-150x150.jpg";
title = "Cutting-Edge CSS Features You Can Use Today";
url = "http://blog.teamtreehouse.com/cutting-edge-css-features-can-use-today";
},
{
author = "Faye Bridge";
date = "2014-10-10 09:00:45";
id = 24230;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/09/Nick-Bryan-150x150.jpg";
title = "After Just 6 Months Learning Nick is a full-time Web Developer";
url = "http://blog.teamtreehouse.com/6-months-nick-now-full-time-web-developer-major-computing-firm";
},
{
author = "Pasan Premaratne";
date = "2014-10-09 13:59:23";
id = 24250;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/Screen-Shot-2014-10-06-at-5.57.16-PM-150x150.png";
title = "Making a Network Request in Swift";
url = "http://blog.teamtreehouse.com/making-network-request-swift";
},
{
author = "Zac Gordon";
date = "2014-10-09 09:21:29";
id = 24278;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/wordpress_themes-150x150.jpg";
title = "New Course: WordPress Theme Development";
url = "http://blog.teamtreehouse.com/new-course-wordpress-theme-development";
},
{
author = "Dave McFarland";
date = "2014-10-08 13:47:55";
id = 24255;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/Screen-Shot-2014-10-06-at-1.02.40-PM-150x150.png";
title = "How to Install Node.js and NPM on a Mac";
url = "http://blog.teamtreehouse.com/install-node-js-npm-mac";
},
{
author = "Jason Seifer";
date = "2014-10-07 16:15:00";
id = 24273;
thumbnail = "http://blog.teamtreehouse.com/wp-content/uploads/2014/10/html5-device-mockups-150x150.jpg";
title = "Device Mockups";
url = "http://blog.teamtreehouse.com/device-mockups";
}
);
status = ok;
}
An example JSON file that was parsed.
Loop through the dictionary:
for(NSString *key in dataDictionary) {
id myObject = [dataDictionary objectForKey:key];
//do something with myObject
}
An NSDictionary is not an ordered collection, so there is no guarantee that looping through a dictionary as shown above will always loop through the keys in the same order. Apple doesn't provide an ordered dictionary with Cocoa/Cocoa Touch, and generally it is a bad idea to subclass NSDictionary or NSMutableDictionary as they are part of a class cluster.
Looking at the text from your example, posts is actually an array full of dictionaries. Assuming all the keys in your example are constant across the JSON files that you will be fetching, you could retrieve it using
NSArray *posts = [dataDictionary arrayForKey:#"posts"];
This array already appears to be ordered by date. You could then get the title for each post
for(int i = 0; i < [posts count]; i++) {
NSString *title = [((NSDictionary *)(posts[i])) objectForKey:#"title"];
//do something with title
}
1) Do you know the data is an array?
the JSON file has an array with two keys,...
value from one of these keys at a specific index...
This is somewhat of a mixed metaphor for me. When I have a JSON Array or NSArray, I tend to only think of indices (since that how arrays are ordered), and when I have JSON Objects or NSDictionaries, I tend to think of keys.
So, does the return value look like this:
[ "cat", 1, "a" ]
or does the data look like this:
{
"cat": {
"count": 1,
"tag": "a"
}
}
The first example is an Array with 3 elements; the second is an Object with 1 member that itself has 2 members.
2) If the data is correctly parsed as either an NSArray, or NSDictionary ...
Then you simply need to extract the data you want, with the accessors available on either container.
E.g.
NSArray *a = ...
[a firstObject];
[a objectAtIndex:0]; // same as above
NSDictionary *d = ...
d[#"memberName"];
[d objectForKey:#"memberName"]; // same as above
You'll want to actually save that data, or pass it to be processed, instead of just invoking the accessor.
UPDATE: based on the example data updated in the question.
One method is that you could extract the data both a bit manually, and iteratively.
NSDictionary *dataDictionary = ...
NSInteger count = [[dataDictionary objectForKey:#"count"] integerValue];
NSInteger countTotal = [[dataDictionary objectForKey:#"count_total"] integerValue];
NSInteger pagesCount = [[dataDictionary objectForKey:#"pages"] integerValue];
NSString *status = [dataDictionary objectForKey:#"status"];
NSArray *posts = [dataDictionary objectForKey:#"posts"];
for (NSDictionary *post in posts) {
for (NSString *key in post) {
NSLog(#"%#: %#", key, post[key]);
}
}
When you log dataDictionary, unquoted elements that are clearly strings are strings, elements in quotes are strings, integers and other numbers are likely usable numbers, but they may be strings (depends on return format), the date will be a string (and you can use NSDate and NSDateFormatter to pretty print it), status is just a string; for posts, the '(' and ')' wrap an array, and '{','}' wrap dictionaries.
UPDATE 2:
If you really want to do advanced searching, you can use NSPredicate to filter NSDictionary's or NSArray's. For example, something like the following would work:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDictionary *data = #{
#"stuff": #1,
#"posts": #[
#{ #"id": #1, #"title": #"one" },
#{ #"id": #2, #"title": #"two" },
#{ #"id": #3, #"title": #"three" },
#{ #"id": #4, #"title": #"four" },
#{ #"id": #5, #"title": #"five" },
]
};
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.id == %#", #4];
NSString *title = [[data[#"posts"] filteredArrayUsingPredicate:predicate] firstObject];
NSLog(#"title: %#", title);
}
}
prints
title: {
id = 4;
title = four;
}
You can use JSONModel Framework
JSONModel is a data model framework for iOS and OSX. It's written in Objective-C and helps you in several different ways.
You can read more about its key features below:
Rapidly write model code
Validation of the model's input
Atomic data
Type casting between JSON and Obj-C
Built-in data transformers
Custom data transformers
Model cascading
Convert back & forth from/to JSON
Persist model state in memory or file
Create models straight from the Internet
Automatic model compare methods
Note Please make sure your properties name match with key name in JSON
JSONModel Framework
GitHub Link

Sitecore Mobile SDK: how to read the value from a linkedItem in a DropLink

I am creating a native IOS app by using the Sitecore Mobile SDK. So far I am able to read the items I need but I got stuck on reading the fieldvalue from a linked item in a Droplink field.
I use this code:
SCApiContext* context = [SCApiContext contextWithHost: #"http://<myhost>/-/item"];
SCItemsReaderRequest* request = [ SCItemsReaderRequest new ];
request.requestType = SCItemReaderRequestQuery;
request.request = #"/sitecore/content/Home/descendant::*[##templatename='Content item']";
request.flags = SCItemReaderRequestReadFieldsValues;
request.fieldNames = [ NSSet setWithObjects: #"Content title", #"Content author", #"Content introduction", #"Content date", #"Content body" , nil ];
[context itemsReaderWithRequest: request]( ^(id result, NSError* error)
{
NSArray* items = result;
for (SCItem* item in result)
{
// get the author
__block NSString *author = #"empty";
SCField *dropLinkField = [item fieldWithName: #"Content author"];
[dropLinkField fieldValueReader]( ^(id result, NSError *error)
{
if (!error)
{
SCItem *linkedItem = result;
// TODO: author is not yet filled
NSSet *fieldsSet = [NSSet setWithObjects:#"Firstname", nil];
// this method seems to be skipped
[linkedItem fieldsReaderForFieldsNames:fieldsSet]( ^(id result2, NSError *error2)
{
if (!error2)
{
NSDictionary *fields = result2;
SCField *field_ = [fields objectForKey: #"Firstname"];
author = field_.rawValue;
}
});
}
});
}
}
The original item is read and I can read the field values of the droplink field. It also seems that I can read the linked Item, because I can write it's itempath to the log. But when I try to read a field from the linked item, it fails and the "fieldsReaderForFieldsNames" method seems to be skipped.
I'm obviously doing something wrong here, but seem to overlook the issue...
EDIT:
I forgot to mention that I use Sitecore 7, not sure if it makes a difference.
I have added the lines above that creates the SCApiContext and SCItemReaderRequest.
I use anonymous access and in the "site settings" I use
itemwebapi.mode="StandardSecurity"
itemwebapi.access="ReadOnly"
itemwebapi.allowanonymousaccess="true"
I just thought that I found the issue, because I did not set the Field Remote Read rights on several fields. However, setting that permission did not resolve it and other fields without the Field Remote Read set, did return in the API.
Sitecore iOS SDK operations (from the list below) are executed asynchronously on the background operation queue.
* fieldValueReader
* fieldsReaderForFieldsNames
This does not guarantee that author data is downloaded at the moment you are accessing it.
Please use downloaded items and fields in the completion callback block to ensure they exist on your iPhone.
[linkedItem fieldsReaderForFieldsNames:fieldsSet]( ^(id result2, NSError *error2)
{
NSLog(#"Read author field");
if (!error2)
{
NSLog(#"No error");
NSDictionary *fields = result2;
SCField *field_ = [fields objectForKey: #"Firstname"];
author = field_.rawValue;
// Now all required fields will
// definitely be downloaded by the time you create a blog item
NSLog(#"voornaam: %#", author);
ParTechBlogItem *blogItem;
blogItem = [[ParTechBlogItem alloc] initWithTitle:[item fieldValueWithName:#"Content title"]
date:[item fieldValueWithName:#"Content date"]
intro:[item fieldValueWithName:#"Content introduction"]
author:author
text:[item fieldValueWithName:#"Content body" ]];
[weakSelf addBlogItem:blogItem];
}