I'm getting results to show in the leaderboard in sandbox mode, but I expected each result to appear. Only one result is showing. Is that normal? The sort option to sort from highest to lowest seems to imply multiple results should show up. What does show up is my high score, which does update if the score is surpassed.
Only one result shows whether I present a VC thus:
- (void) presentLeaderboards {
GKGameCenterViewController* gameCenterController = [[GKGameCenterViewController alloc] init];
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
gameCenterController.gameCenterDelegate = self;
[self presentViewController:gameCenterController];
}
or if I use the Game Center app.
Here is how I am submitting scores:
-(void) submitScore:(int64_t)score
category:(NSString*)category {
if (!_gameCenterFeaturesEnabled) {
DLog(#"Player not authenticated");
return;
}
GKScore* gkScore =
[[GKScore alloc]
initWithLeaderboardIdentifier:category];
gkScore.value = score;
[GKScore reportScores:#[gkScore] withCompletionHandler:^(NSError *error) {
if (error) {
// handle error
}
}];
}
"The sort option to sort from highest to lowest seems to imply multiple results should show up"
This doesn't mean that. It means that all scores submitted (By all Players) will be sorted by Highest to Lowest or Lowest to Highest.
Since you have selected High Score in Leaderboard setup on iTunes Connect. It will update player's score only if it is higher than previous one and will not save other scores submitted.
Related
I am trying to add Core Spotlight search to my app. I have imported
#import <MobileCoreServices/MobileCoreServices.h>
#import <CoreSpotlight/CoreSpotlight.h>
then added
[self setupCoreSpotlightSearch];
under viewDidLoad. Then added the following code.
- (void)setupCoreSpotlightSearch
{
if ([CSSearchableIndex isIndexingAvailable]) {
NSLog(#"Spotlight indexing is available on this device");
CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeImage];
// Set properties that describe attributes of the item such as title, description, and image.
NSString *title = _locationTittle;
attributeSet.title = title;
attributeSet.contentDescription = [NSString stringWithFormat:#"%#",title];
attributeSet.keywords = #[ title ];
// Create an attribute set for an item
UIImage *image = _locationImage;
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(image)];
attributeSet.thumbnailData = imageData;
// Create a searchable item, specifying its ID, associated domain, and the attribute set you created earlier.
CSSearchableItem *item;
NSString *identifier = [NSString stringWithFormat:#"%#",attributeSet.title];
item = [[CSSearchableItem alloc] initWithUniqueIdentifier:identifier domainIdentifier:#"com.mybundleid.productname.search" attributeSet:attributeSet];
// Index the item.
[[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:#[item] completionHandler: ^(NSError * __nullable error) {
if (!error) {
NSLog(#"error = %#", error);
}];
} else {
NSLog(#"Spotlight indexing is not available on this device");
}
}
Note I have added my bundle id with .search after it.
When I try to search for the name of the landmark, which is _locationTittle nothing shows up. I cannot understand this and have tried to follow other tutorial and example code. Can someone please help.
Update. This seems to be working and just loads the app when pressed as if the app was just normally loaded. I added it into my view controller for looking in more detail at the landmark. This means that the user has to have clicked the landmark maker for it to start showing up in spotlight. Ideally I would like it to show all the landmarks without the user having to open them all for them to show in spotlight, then they can press the landmark for that specific landmarks detail view to be loaded. In my view controller where all the data is handled then passed to my detail view of the landmark is this code, which passes the data into the detail view:
- (void)locationClicked:(ARGeoCoordinate *)coordinate{
NSLog(#"%#", coordinate);
[Answers logCustomEventWithName:coordinate.locationTittle
customAttributes:#{}];
//user has tap on location, we need to show the location details view here
detailViewController *vc = (detailViewController*)[[self storyboard] instantiateViewControllerWithIdentifier:#"detailViewController"];
vc.locationImage = coordinate.imgLocation;
vc.locationTittle = coordinate.locationTittle;
vc.locationTxt = coordinate.locationTxt;
vc.PhotoCreditText = coordinate.photoCreditTittle;
[self.navigationController pushViewController:vc animated:YES];
}
This all comes from:
- (NSMutableArray *)geoLocations {
NSMutableArray *locationArray = [[NSMutableArray alloc] init];
ARGeoCoordinate *tempCoordinate;
CLLocation *tempLocation;
UIImage *locationImage;
// test locations
tempLocation = [[CLLocation alloc] initWithLatitude:53.712371 longitude:-1.882742];
tempCoordinate = [ARGeoCoordinate coordinateWithLocation:tempLocation locationTitle:#"Wainhouse Tower"];
locationImage = [UIImage imageNamed:#"WainhouseTower.jpg"];
[tempCoordinate setImgLocation:locationImage];
[tempCoordinate setLocationTxt:#"Wainhouse Tower is a folly in the parish of King Cross. At 275 feet (84 m), it is the tallest structure in Calderdale and the tallest folly in the world, and was erected in the four years between 1871 and 1875. The tower was completed on 9 September 1875, at a cost of £14,000. The main shaft is octagonal in shape and it has a square base and 403 steps leading to the first of two viewing platforms.\n\nOne driving force behind the erection of the viewing platforms was a long standing feud between Wainhouse and his neighbour, landowner Sir Henry Edwards. Edwards had boasted that he had the most private estate in Halifax, into which no one could see. As the estate was on land adjacent to the chimney's site, following the opening of the viewing platforms, Edwards could never claim privacy again.\n\nThe tower was designed by architect Isaac Booth as a chimney to serve the dye works owned by John Edward Wainhouse (1817–1883). The height of the chimney was to satisfy the Smoke Abatement Act of 1870 which required a tall chimney to carry smoke out of the valleys in which the factories were built. A much simpler chimney would have satisfied the requirements but Wainhouse insisted that it should be an object of beauty.\n\nIn 1874 John Wainhouse sold the mill to his works manager who refused to pay the cost of the chimney's construction so Wainhouse kept the tower for himself and used it as an observatory. Booth left after a dispute and was replaced by another local architect, Richard Swarbrick Dugale, who is responsible for the elaborate galleries and the corona dome at the top.\n\nThe tower is open to the public during bank holidays, and is a Grade II listed building.\n\n\nMaterial has been used from the Wikipedia article http://en.wikipedia.org/wiki/Wainhouse_Tower see that article's history for attribution.\n\nWainhouse Tower by James Preston https://flic.kr/p/cCMB2A is licensed under CC BY 2.0"];
[tempCoordinate setPhotoCreditTittle:#"See attribution below"];
[tempCoordinate setLocationTittle:#"Wainhouse Tower"];
[locationArray addObject:tempCoordinate];
return locationArray;
}
How do I make it so core spotlight works so the detail view doesnt have to have been loaded for each landmark by using the above code, and when the spotlight search is pressed it calls the above code?
I am upgrading my Application written a year ago for iOS 6 to iOS 7/8 and I am getting this EXC_BAD_ACCESS error which never occurred in my old version.
In my application I am trying to fetch certain contact information like first name, last name, phone numbers, photo. Application flow is as follow:
1) Click on a button, presents address book.
2) Select any contact.
3.1) If contact has only one phone number, update the label.
3.2) If contact has multiple phone number, represent them in action sheet and whatever number user selects update that number to UILabel.
Now, if a contact has a single phone number application works fine without crash. i.e. 1-->2-->3.1 path. But if a contact has multiple phone and as soon as one contact number is selected from action sheet it crashes at this line.
CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));
Detail Code
1) Select a contact
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
sharedSingleton.personGlobal = nil;
sharedSingleton.personGlobal=person; // ====> Save a ABRecordRef object globally.
//^^^ Could this be a culprit? I tried to make it private variable also at first.
[self displayAndVerifyPerson]; // No 2 below.
[self dismissViewControllerAnimated:YES completion:^{
}];
return NO;
}
2) Will check how many phone nos person has got. 0/1/>1.
If 0 show no phone no error.
If 1 phone update label by calling updateLabel.
If >1 represent action sheet for user to select number. And on clickedButtonIndex call updateLabel.
-(void)displayAndVerifyPerson
{
ABMultiValueRef phoneNumbers = ABRecordCopyValue(sharedSingleton.personGlobal,kABPersonPhoneProperty); //ABRecordRef which globally saved.
globalContact=nil; //NSString to store selected number. Works fine.
//self.personGlobal=person;
NSArray *phoneNumberArray = (__bridge_transfer NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbers);
CFRelease(phoneNumbers);
if (ABMultiValueGetCount(phoneNumbers) > 0){ //Check if a contact has any number
NSLog(#" Number--> %#",phoneNumberArray); //Prints numbers correct whether no of contacts are 0/1/>1.
if ([phoneNumberArray count]==1){ //If exactly one contact number no problem.
globalContact = [phoneNumberArray objectAtIndex:0];
NSLog(#"--> %#",globalContact);
[self updateLabel]; // No 3 Below.
}
// We have multiple numbers so select any one.
else{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Select Number"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
actionSheet.delegate=self;
actionSheet.tag=0;
for(int i=0;i<[phoneNumberArray count];i++){
[actionSheet addButtonWithTitle:[phoneNumberArray objectAtIndex:i]];
}
[actionSheet addButtonWithTitle:#"Cancel"];
actionSheet.destructiveButtonIndex = actionSheet.numberOfButtons - 1;
actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
if ([window.subviews containsObject:self.view])
[actionSheet showInView:self.view];
else
[actionSheet showInView:window];
}
}
else{ //No contact found. Display alert.
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"No contact numebr found."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
return;
}
}
3) Fetch first name, Last name, Image from ABRecordRef Object.
-(void)updateLabel{
// ----------------- Get First Name From Global ABRecordRef personGlobal---------------------
CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));
^^^^^^^^^^^^^^^^^^^^^^
Crashes only when `updateLabel` called from Actionsheet delegate `clickedButtonAtIndex`
NSString *fName = (NSString *)CFBridgingRelease(firstNameCF);
if ([fName length]==0){
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Contact name not found."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
return;
}
self.lblFirstName.text = fName; //Set label with first Name.
self.lblHomePhone.text = self.globalContact;//Set number label.
}
4) Actionsheet Delegate
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *buttonTitle=[actionSheet buttonTitleAtIndex:buttonIndex];
if(actionSheet.tag==0){
//Printing multiple phone numbers which works and prints perfect.
NSLog(#"Lets see what you got: ===> %#",buttonTitle);
if([buttonTitle isEqualToString:#"Cancel"])
return;
globalContact=buttonTitle; // Save contact to NSString for later use.
[self updateLabel]; // No. 3.
}
}
Extra Notes:
1) Questions I looked for solution(Just 3 of many).
i) ABRecordCopyValue() EXC_BAD_ACCESS Error
ii) EXC_BAD_ACCESS when adding contacts from Addressbook?
iii) kABPersonFirstNameProperty… trowing EXC_BAD_ACCESS
2) Sample project on dropbox if someone is generous/curious enough and wants to run and check.
3) My doubts regarding this error:
The same code works for a current App (Written for iOS 6) which is on App Store but crashes for iOS 7.
Could be due to Memory management of Core Foundation. I tried to release Core Foundation object wherever I used as ARC does not take care of them. But if that is a case then it should also crash while contact has only one phone number.
THREAD ISSUE? Since application only crashed shen contact has more than one phone number, I believe action sheet delegate method clickedButtonAtIndex running on background thread and something is going wrong? (Just a random guess!)
I have tried to make my question easy and informative at my best. Any suggestion, comment or solution will be appreciated as I have been trying to get rid of this issue for last 3 days. Thanks!
you deal with CoreFoundation:
sharedSingleton.personGlobal=person;
=>
since it isn't an arc object, you have to retain it
CFRetain(person);
sharedSingleton.personGlobal=person;
AND release it once done
- dealloc {
CFRelease(sharedSingleton.personGlobal);
}
Ignoring the weirdness of a lot of this code, the fundamental issue is that you are not retaining a value that you intend to use beyond the scope it is presented in. Specifically, I am referring to the person variable in section number 1. You don't retain this variable, and so it is free to be released at any time after the scope ends (which it likely does). Therefore, once you get around to calling updateLabel it is simply a dangling pointer. To fix this, you should make it a strong variable.
But wait a minute...that is only for Objective-C objects, so you need to do a little more decorating of the property. You can add __attribute__((NSObject)) to make this type behave as if it were an NSObject and subject to ARC. I can't find documentation about this anymore, but here is a reference from an old Apple Mailing List Thread
In my game (I'm using SpriteKit, and therefore only support iOS 7), when a player reaches his first 10 points, he is awarded with an achievement. I've implemented the achievement method as follows:
-(void) First10Points
{
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: #"Achievement_First10Points"];
if (achievement)
{
achievement.showsCompletionBanner = YES;
achievement.percentComplete = 100.0;
NSArray *achievements = [NSArray arrayWithObjects:achievement, nil];
[GKAchievement reportAchievements:achievements withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"Error in reporting achievements: %#", error);
}
}];
}
}
This works fine and the achievement is indeed earned at 10 points, with the game center banner indicating this to the player during the game. However, when the banner disappears it reappears after a second or so and continues to do so until i terminate the game. The game can still be played while it does this loop thing. I can't seem to understand why it does this and I have not come across this problem while searching the web.
Anyone an idea? Or should I implement my achievements in another way?
One possibility is that you're calling the First10Points method multiple times. You should check if the player has already reached the 10 points achievement before presenting the achievement again. If they have indeed already reached it, then don't call the method.
Try adding a variable like BOOL first10 = NO; When you run your check (score == 10), set first10 = YES; Everytime before you call First10Points, ensure that (first10 == NO)
I'm currently developing a turn based game using Game Center to handle the online functionalities (for matchmaking and turns handling).
I'm using two sandbox accounts - one on my 3gs and one on the ios Simulator.
I've been testing my app using the GKTurnBasedMatchMakerViewController to do the match making for a while without any problems, but I'm now stuck with an issue:
Every time I want to invite another player for a new (with either one or the other player), the GKTurnBasedMatchMakerViewController displays a UIAlertView stating :
Could not create game - Please remove an existing game and try again.
The thing is, I've deleted all the matches for each player (none of them has any game in his list (not even a closed game). So none of the user is in any match at the moment.
In my GKTurnBaseMatchMakerViewControllerDelegate the turnBasedMatchmakerViewController:didFailWithError: is not called.
The only called function called in the delegate- when I click the OK button on the UIAlertView - is turnBasedMatchmakerViewControllerWasCancelled:
The only thing I can think of is that my games are actually not removed from GameCenter, but as I'm removing them using the GKMatchMakerViewController UI, I barely think so.
When quitting from a turn-based match I've implemented the turnBasedMatchmakerViewController:playerQuitForMatch: like this:
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController playerQuitForMatch:(GKTurnBasedMatch *)match
{
if ( [self isLocalPlayerCurrentPlayerForMatch:match] ) {
NSData* endData = match.matchData;
for (GKTurnBasedParticipant* participant in match.participants) {
participant.matchOutcome = GKTurnBasedMatchOutcomeWon;
}
match.currentParticipant.matchOutcome = GKTurnBasedMatchOutcomeLost;
[match endMatchInTurnWithMatchData:endData
completionHandler:^(NSError *error) {
if (error) {
NSLog(#"%#",error.description);
}
}];
}
}
(NB: I only have two players in the game)
where isLocalPlayerCurrentPlayerForMatch is:
- (BOOL) isLocalPlayerCurrentPlayerForMatch:(GKTurnBasedMatch*)match
{
return [[[GKLocalPlayer localPlayer] playerID] isEqualToString:match.currentParticipant.playerID];
}
Has anyone encountered and found a solution to this issue?
Am I doing something wrong here, or is it so obvious I just can't see it?
Thank you very much for any comments that would help me find the root of that issue.
Update
Thanks to #kaan-dedeoglu I managed to know that both users had an empty list of matches (consistent with the displayed state).
I also created a third Sandbox account.
Naming the two first accounts A and B, C the third one.
State 1:
A and B are not linked to any match.
A and B are both getting the "Could not create game" error while creating any game (A invites B, A||B invites other player, A||B creates new automatch).
State 2:
C (working account) can invite B and normally plays a party with B.
C (working) can invite B for another simultaneous party
C (working) invites A to play.
A can't play (can't access the list of current matches, the GKTurnBasedMatchMakerViewController directly goes to the creation of a new game).
C is not working anymore.
A, B and C are now stuck in "Could not create game" error.
As a complement here is how I initialize my GKTurnBasedMatchMakerViewController, but I don't see that being wrong.
- (void) displayMatchMakerVC
{
if (! [[GKLocalPlayer localPlayer] isAuthenticated] ) return;
GKMatchRequest* request = [[[GKMatchRequest alloc] init] autorelease];
int nbPlayers = 2;
request.minPlayers = nbPlayers;
request.maxPlayers = nbPlayers;
GKTurnBasedMatchmakerViewController* matchMakerVC = [[[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
matchMakerVC.turnBasedMatchmakerDelegate = self;
matchMakerVC.showExistingMatches = YES;
[[CCDirector sharedDirector] presentModalViewController:matchMakerVC animated:YES];
}
NB: I'm not using ARC, could that be related to a memory issue? I'm not really a memory management guru, but it seems correct to my understanding.
Any idea of how this could be related to my code and not to game center?
Thank you very much for any answer that could help me go further.
Update 2: turnbasedMatchmakerViewController:didFindMatchMethod:
Here's my turnbasedMatchmakerViewController:didFindMatchMethod: method.
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
BLTheme* theme = [[[BLGameConfig sharedConfig] localPlayer] userTheme];
GameSceneRemoteGCLoader* loader = [[GameSceneRemoteGCLoader alloc] initWithGKMatch:match andTheme:theme];
[viewController dismissViewControllerAnimated:NO completion:^{}];
[[CCDirector sharedDirector] replaceScene:loader];
}
When I'm launching an automatch it's launching the exact same error "Could not create game - Please remove an existing game and try again.".
This may or may not be the solution to your problem, but I had a similar issue and solved it in the following way.
It seems that either by default, or somehow, Game Center treats apps with differing CFBundleVersion (build number, not version number, or CFBundleShortVersionString) values as incompatible with one another, and thus does not show matches between apps with incremented build numbers. (Often, developers increment this number as new ad hoc builds or stable releases are distributed during development, so this is quite unfortunate).
To find and remove the "missing" games, I decremented my CFBundleVersion value (which revealed the games), and then deleted the offending matches.
Alternatively, tweaking some settings in iTunes Connect seems to have removed this CFBundleVersion incompatibility. It takes a while to propagate, but I think what did it was tapping on my app, tapping on View Details, making sure the Game Center switch is set to "Enabled", and making sure there is an item in the "Multiplayer Compatibility" table. You could also play with the possibilities within the "Manage Game Center" button from the original app screen, but I think the "Multiplayer Compatibility" setting is what finally allowed me to see all the "old" matches that were previously hidden.
Good luck!
Just to make sure: In both these devices, add these lines in your authentication completion handler and run it once. (then you can comment it out).
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(^)(NSArray *matches, NSError *error) {
for (GKTurnbasedMatch *match in matches) {
[match removeWithCompletionHandler:NULL];
}
}];
This will ensure that all games are removed from your playerID.
It's ridiculous . You don't have to remove an existing match to create a new match. I'm developing a game like this and it actually works.
The following worked for me. First I ran the app on the device for each player, calling quitAllMatches. Then I ran the app again on each device, calling removeAllMatches.
In the long run, it has to be better to clean them up as you go along. But this solved the immediate problem.
-(void) quitAllMatches {
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray* matches, NSError* error) {
for (GKTurnBasedMatch* match in matches) {
GKTurnBasedParticipant* participant = match.currentParticipant;
NSString* playerID = participant.playerID;
NSString* localPlayerID = [GKLocalPlayer localPlayer].playerID;
if ([playerID isEqualToString: localPlayerID]) {
NSArray* participants = match.participants;
for (GKTurnBasedParticipant* participant in participants) {
participant.matchOutcome = GKTurnBasedMatchOutcomeTied;
}
NSData* data = [NSData data];
[match endMatchInTurnWithMatchData: data completionHandler:^(NSError* error) {
if (error) {
WJLog(#"did not end -- error %#", [error localizedDescription]);
}
else {
WJLog(#"match ended!");
}
}];
}
}
}];
}
-(void) removeAllMatches {
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray* matches, NSError* error) {
for (GKTurnBasedMatch* match in matches) {
[match removeWithCompletionHandler:^(NSError* error) {
if (error) {
WJLog(#"error: %#", [error localizedDescription]);
}
else {
WJLog(#"removed match");
}
}];
}
}];
}
I am adding Game Center functionality to my app. On the simulator, the app registers and loads the high scores perfectly from the Game Center app and the Leaderboard View in my app. When I try the same thing from an actual device,the console says the score was submitted but the score does not show up in the Game Center App or in the Leaderboard View in my app. No idea why this would be. Any Help would be great. Here is my code on how I am implementing this.
My View Did Load
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error){
if (error ==nil) {
NSLog(#"Success");
} else {
NSLog(#"Fail");
}
}];
How I am submitting my score
-(IBAction)submitMyScore{
//This is the same category id you set in your itunes connect GameCenter LeaderBoard
GKScore *myScoreValue = [[[GKScore alloc] initWithCategory:#"01"] autorelease];
myScoreValue.value = score;
[myScoreValue reportScoreWithCompletionHandler:^(NSError *error){
if(error != nil){
NSLog(#"Score Submission Failed");
} else {
NSLog(#"Score Submitted");
}
}];
}
Anyone have any idea why this is?
I fixed the Issue. I logged out of my game center account and then started the game. It prompted me to create a new game center account and I did. It then put me into sandbox mode and allowed me to view and post scores.