Report achievement progress by increasing it instead of replacing it - objective-c

I have trouble reporting achievements. I would like to do the following:
When I submit an achivment progress like 1%, I would like to increase the actuall progress not replaccing it.
So if I submit 1% and the user allready have like 4% its have to be 5% not 1%. Is there any way to do this?
Here my actual reporting code:
+(void)reportachivementwithidenfitier:(NSString *)identifier percentcompleted:(float)percent{
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
if ([defaults boolForKey:#"localplayeravailable"]) {
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: identifier];
if (achievement)
{
achievement.percentComplete = percent;
achievement.showsCompletionBanner=YES;
NSArray *achivments=[[NSArray alloc]initWithObjects:achievement,nil];
[GKAchievement reportAchievements:achivments withCompletionHandler:^(NSError *error){
if (error!=nil) {
NSLog(#"Achievement Submission failed with error:%#",[error description]);
}
else
{
NSLog(#"Achievement Succesfully reported");
}
}];
}
}

on this line:
achievement.percentComplete = percent;
you are setting it to a new value. Simply add the value to it, like so:
achievement.percentComplete += percent;

Related

CMAltimeter detect take off and landing

I would like to know how I can create a notification when the altitude is increasing and a notification when the altitude decreasing.
I already tried this code but I've no idea what to do next.
- (CMAltimeter *)altimeter {
if (!_altimeter) {
_altimeter = [[CMAltimeter alloc]init];
}
if ([CMAltimeter isRelativeAltitudeAvailable]) {
CMAltimeter* altimeter = [[CMAltimeter alloc] init];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[altimeter startRelativeAltitudeUpdatesToQueue:queue withHandler:^(CMAltitudeData* altitudeData, NSError* error) {
}];
}
return _altimeter;
}
You pull out the data each time there's an update:
[altimeter startRelativeAltitudeUpdatesToQueue:queue
withHandler:^(CMAltitudeData* altitudeData, NSError* error)
{
// Put your data-handling code here -- for example,
// if your display has an element userRelAltitude
// that displays text:
float relAltitude;
relAltitude = altitudeData.relativeAltitude.floatValue;
self.userRelAltitude.text = [NSString stringWithFormat:#"%.0f m", relAltitude];
}];
Then you can compare each value to the previous one to see if it's increasing or decreasing and display an appropriate notification.

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};

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];
}

NSFetchRequest doesn't give back anything

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.

Change the displayed unit instantly after user changes it using UISegmentedControl

I have a UISegmentedControl that handles the setting of the temperature unit (ºC/ºF).
It works correctly but if the user changes the setting, the displayed unit won't change (refresh itself) unless the user navigates to another View Controller and then back to the one in question.
I'm using
- (void)viewWillAppear:(BOOL)animated
{
//Gets user setting choice for Fahrenheit or Celsius
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
temperatureSetting = [[preferences valueForKey:#"temperature setting"]intValue];
//For getting the current value of temperature from the JSON data
NSInteger temp = [[conditions objectForKey:#"temperature"]intValue];
//For checking the user's setting to display either Fahrenheit or Celsius
if (temperatureSetting == 0) {
temp = (temp * 1.8) + 32;
temperatureLabel.text = [NSString stringWithFormat:#"%i°F", temp];
} else {
temperatureLabel.text = [NSString stringWithFormat: #"%iºC", temp];
}
}
- (IBAction)temperatureSelection:(UISegmentedControl *)sender {
//Used to save settings
NSUserDefaults *temperaturePreference = [NSUserDefaults standardUserDefaults];
//To check which segment is selected and save the value but also remember it
if ([sender selectedSegmentIndex] == 0) {
[temperaturePreference setInteger:0 forKey:#"temperature setting"];
[selectedSegment setInteger:0 forKey:#"segment setting"];
} else if ([sender selectedSegmentIndex] == 1) {
[temperaturePreference setInteger:1 forKey:#"temperature setting"];
[selectedSegment setInteger:1 forKey:#"segment setting"];
}
//To make sure the setting is saved
[temperaturePreference synchronize];
[selectedSegment synchronize];
}
How can I make it change the displayed unit instantly after the user changes it?
Thank You!
Move the code inside viewWillAppear: to a separate method. Call that method when the user changes the settings.
Try to add that line of code before the end of - (IBAction)temperatureSelection:(UISegmentedControl *)sender method.
- (IBAction)temperatureSelection:(UISegmentedControl *)sender{
//..etc
temperatureSetting = [[temperaturePreferences valueForKey:#"temperature setting"]intValue];
if (temperatureSetting == 0) {
temp = (temp * 1.8) + 32;
temperatureLabel.text = [NSString stringWithFormat:#"%i°F", temp];
} else {
temperatureLabel.text = [NSString stringWithFormat: #"%iºC", temp];
}
[self.view setNeedsDisplayInRect:temperatureLabel.frame];//probably is not needed it forces the view to redraw itself as it mark as dirty
}