Game Center report in iOS 7 (Deprecated) - ios7

My IDE show warning "Obsolete: Deprecated in iOS 7.0" this methods:
(GKScore) ReportScore()
(GKAchievement) ReportAchievement()
This methods it works on iOS 7, but use not problem? Exist other methods on iOS 7?
Thanks!

I used this method to report score in game center and it works.
-(void)reportScore
{
if(isIOS7)
{
// Create a GKScore object to assign the score and report it as a NSArray object.
GKScore *score = [[GKScore alloc] initWithLeaderboardIdentifier:_leaderboardIdentifier];
score.value = _score;
[GKScore reportScores:#[score] withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"score reporting error : %#", [error localizedDescription]);
}
else
{
NSLog(#"score reported.");
}
}];
}
else
{
GKScore *scoreReporter = [[GKScore alloc] initWithCategory:_leaderboardIdentifier];
scoreReporter.value = _score;
scoreReporter.context = 0;
[scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
// Do something interesting here.
if (error != nil) {
NSLog(#"score reporting error : %#", [error localizedDescription]);
}
else
{
NSLog(#"score reported.");
}
}];
}
}

Objective-C methods start with a lower-case method. They are not called with rounded brackets. So, I:
opened the documentation for GKScore;
looked for anything deprecated and found -reportScoreWithCompletionHandler:;
saw that +reportScores: withCompletionHandler: isn't deprecated.
And saw pretty much the same thing with the singular instance method being deprecated in favour of the collection class method in GKAchievement.
So: just use the collection methods. Deprecated methods hang around being unsupported for a while and then disappear. You can find out what's currently supported, very very quickly, by reading the documentation.

Related

Reporting score to gamecenter for ios7

According to https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/LeaderBoards/LeaderBoards.html
Reporting score to gamecenter in ios7 should be done using
[GKLeaderboard reportScores:scores withCompletionHandler:^(NSError *error) {
//Do something interesting here.
}];
However, I could not find any reference to this method in GKLeaderboard.
The method does not exist here:
https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLeaderboard_Ref/Reference/Reference.html
GKLeaderboard.h does not contain a reportScores method also.
The former way of reporting score using GKScore's reportScoreWithCompletionHandler method had been deprecated so I am reluctant to use that.
Does anyone know whats the correct way to report score to gamecenter in ios7?
I can confirm that the reportScores:withCompletionHandler: method does work; I'm using it in one of my apps. It's located in the header file GKScore.h. This is how I'm using it:
- (void) reportHighScore:(NSInteger) highScore {
if ([GKLocalPlayer localPlayer].isAuthenticated) {
GKScore* score = [[GKScore alloc] initWithLeaderboardIdentifier:MY_LEADERBOARD_ID];
score.value = highScore;
[GKScore reportScores:#[score] withCompletionHandler:^(NSError *error) {
if (error) {
// handle error
}
}];
}
}

Game Center turn-based match data is not saved and/or read

I am planning to develop a turn-based game and is trying to understand how to communicate with Game Center and send and receive mach data. I have read about it and tested this for days now and just cannot get it to work as planned.
The only thing i try to do with the code below is to be able to save and then read the mach data. I am using two sandbox Game Center accounts for the turns.
The turns are sending the same data by pressing "endTurn" button. Every time i run the actual user is authenticated and the app is set up correctly (i believe).
This is a test app without any other purpose than test what i stated. Below is the code i use for the match data processing.
I would really appreciate any ideas and tips on what i may do wrong. Before i started serious testing i did post a similar question but that did not solve this problem, https://stackoverflow.com/questions/14447392/start-gamecenter-turn-based-match-and-initiate-match-data-for-the-very-first-tim.
I also try to catch the participants but with no success, which may mean that it is the problem when processing the completionhandler.
-(IBAction)endTurn:(id)sender {
[_gameDictionary setObject:#"The Object" forKey:#"The Key"];
NSLog(#"_gameDictionary: %#", _gameDictionary);
NSData *data = [NSPropertyListSerialization dataFromPropertyList:_gameDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:nil];
GKTurnBasedParticipant *nextPlayer;
if (_match.currentParticipant == [_match.participants objectAtIndex:0]) {
nextPlayer = [[_match participants] lastObject];
} else {
nextPlayer = [[_match participants]objectAtIndex:0];
}
NSLog(#"_match.currentParticipant: %#", _match.currentParticipant);
[self.match endTurnWithNextParticipant:nextPlayer matchData:data completionHandler:^(NSError *error) {
if (error) {
NSLog(#"An error occured updating turn: %#", [error localizedDescription]);
}
[self.navigationController popViewControllerAnimated:YES];
}];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
_gameDictionary = [[NSMutableDictionary alloc]init];
[self.match loadMatchDataWithCompletionHandler:^(NSData *matchData, NSError *error) {
NSDictionary *myDict = [NSPropertyListSerialization propertyListFromData:_match.matchData mutabilityOption:NSPropertyListImmutable format:nil errorDescription:nil];
[_gameDictionary addEntriesFromDictionary: myDict];
if (error) {
NSLog(#"loadMatchData - %#", [error localizedDescription]);
}
}];
NSLog(#"_gameDictionary: %#", _gameDictionary);
}
Output:
"gk-cdx" = "17.173.254.218:4398";
"gk-commnat-cohort" = "17.173.254.220:16386";
"gk-commnat-main0" = "17.173.254.219:16384";
"gk-commnat-main1" = "17.173.254.219:16385";
}
2013-02-11 22:44:11.707 GC_test1[8791:14f03] _gameDictionary: {
}
2013-02-11 22:44:13.894 GC_test1[8791:14f03] _gameDictionary: {
The Object = The Key;
}
2013-02-11 22:44:13.894 GC_test1[8791:14f03] _match.currentParticipant: (null)
The fact that _match.currentParticipant evaluates to nil is troubling. I suspect that _match was never initialized, or is nil, or that it was not obtained from a Game Center facility such as loadMatchesWithCompletionHandler:, the GKTurnBasedMatchmakerViewController, or using findMatchForRequest:withCompletionHandler:.
For a new match, if created through any of these facilities, currentParticipant would be guaranteed to represent the local player. You are not allowed to instantiate a GKTurnBasedMatch yourself.
To resolve this issue at least for testing, you could assign a new _match from within the completion handler of findMatchForRequest:withCompletionHandler:. Only then should you be allowed to press your test button.

Why use Try/Catch instead of testing for an NSError

Sometimes you see a piece of iOS - Objective-C code use a Try/Catch structure.
For example this example from: http://docs.xrtml.org/2-1-0/pubsub/ios/ortcclient.html
- (void)viewDidLoad
{
[super viewDidLoad];
// Instantiate OrtcClient
ortcClient = [OrtcClient OrtcClientWithConfig:self];
// Post permissions
#try {
NSMutableDictionary* myPermissions = [[NSMutableDictionary alloc] init];
[myPermissions setObject:#"w" forKey:#"channel1"];
[myPermissions setObject:#"w" forKey:#"channel2"];
[myPermissions setObject:#"r" forKey:#"channelread"];
BOOL result = [ortcClient saveAuthentication:#"http://ortc-developers.realtime.co/server/2.1/" isCLuster:YES authenticationToken:#"myAuthenticationToken" authenticationTokenIsPrivate:NO applicationKey:#"myApplicationKey" timeToLive:1800 privateKey:#"myPrivateKey" permissions:myPermissions];
if (result) {
// Permissions correctly posted
}
else {
// Unable to post permissions
}
}
#catch (NSException* exception) {
// Exception posting permissions
}
// Set connection properties
[ortcClient setConnectionMetadata:#"clientConnMeta"];
[ortcClient setClusterUrl:#"http://ortc-developers.realtime.co/server/2.1/"];
// Connect
[ortcClient connect:#"myApplicationKey" authenticationToken:#"myAuthenticationToken"];
}
Why use such a structure, couldn't you just check for an NSError (indirect) return from the saveAuthentication:isCLuster:authenticationToken:... method like 'regular' Cocoa-Touch code does? For example when reading JSON:
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (error == nil){
NSLog(#"%#", result);
}else{
NSLog(#"%#", [error localizedDescription]);
}
Use try catch where you expect a condition that cannot be recovered from or which may lead to an undefined behaviour such as crash, use NSError where recovereable errors are expected like wrong values from a json object or xml.
You can go throughApple documentation about exception programming.
In general, try-catch is more robust, does not require you to define an exact position of where to test (could be a block) and provides info about the exception.

EXC_BAD_ACCESS during NSFileVersion call to removeOtherVersionsOfItemAtURL: inside coordinated write block

I'm using what seems to be a simple invocation of the NSFileVersion class method removeOtherVersionsOfItemAtURL: inside a coordinated writing block for some iCloud conflict resolution.
When my devices go into 'spaz mode', which is a technical term for repeatedly opening and closing the application on a few devices, an EXC_BAD_ACCESS exception is thrown internally. Code snippet:
- (void)compareVersionChanges:(NSFileVersion *)version {
if (![DataLoader iCloudPreferenceEnabled]) {
NSLog(#"Ignoring iCloud changes (version comparison) based on user preference");
return;
}
NSLog(#"compareVersionChanges");
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^(void) {
NSError *readError = nil;
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:(id)self];
[coordinator coordinateReadingItemAtURL:[version URL] options:0 error:&readError byAccessor:^(NSURL *newURL) {
DataContext *loadedContext = nil;
NSData *data = [NSData dataWithContentsOfURL:newURL];
NSError *e = nil;
loadedContext = [self convertXmlDataToContext:data error:&e];
if (e) {
NSLog(#"Done loading, error: %#", e);
[[DataLoader applicationDelegate] displayError:e];
loadedContext = nil;
}
if (!loadedContext) {
return;
}
id appDelegate = [DataLoader applicationDelegate];
DataContext *inMemoryContext = nil;
if (appDelegate != nil && [appDelegate respondsToSelector:#selector(context)]) {
inMemoryContext = [appDelegate performSelector:#selector(context)];
}
if (inMemoryContext) {
NSLog(#"Performing iCloud context synchronizating...");
DataContextSynchronizer *synchronizer = [[DataContextSynchronizer alloc] init];
ChangeSet *changes = [synchronizer compareLocalContext:inMemoryContext andRemoteContext:loadedContext];
if ([[changes changes] count] > 0) {
[SelectionManager disable];
#synchronized(appDelegate) {
NSLog(#"Applying synchronization changes...");
[synchronizer applyChangeSet:changes toDataContext:inMemoryContext];
NSLog(#"Synchronization changes applied");
}
[SelectionManager enable];
if ([appDelegate respondsToSelector:#selector(setSkipRefreshSave:)]) {
[appDelegate performSelector:#selector(setSkipRefreshSave:) withObject:[NSNumber numberWithBool:YES]];
}
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
[SelectionManager notifyListeners];
});
if ([appDelegate respondsToSelector:#selector(setSkipRefreshSave:)]) {
[appDelegate performSelector:#selector(setSkipRefreshSave:) withObject:[NSNumber numberWithBool:NO]];
}
[self save:[[DataLoader applicationDelegate] context]];
} else {
NSLog(#"No sync changes applicable.");
}
NSError *coordinateWriteRemoveError = nil;
[coordinator coordinateWritingItemAtURL:newURL options:NSFileCoordinatorWritingForDeleting error:&coordinateWriteRemoveError byAccessor:^(NSURL *theURL) {
theURL = [theURL copy];
NSError *removeOtherVersionsError = nil;
[NSFileVersion removeOtherVersionsOfItemAtURL:theURL error:&removeOtherVersionsError];
if (removeOtherVersionsError) {
NSLog(#"Error removing other versions: %#", removeOtherVersionsError);
}
}];
if (coordinateWriteRemoveError) {
NSLog(#"Error occurred coordinating write for deletion of other file versions: %#", coordinateWriteRemoveError);
}
}
}];
if (readError) {
NSLog(#"Done loading (outside block) error: %#", readError);
}
});
}
I thought a little syntax highlighting might make this easier to examine:
Link to image of code snippet and failure stack in Xcode
The error actually occurs on line 1404, and as you can see from the below screenshot, it's deep in Apple code territory.
Link to image of debugger
Before submitting a radar, I thought I'd check here to see if there's something I'm doing wrong? The extra [... copy] on line 1402 was just a quick check to make sure I'm not losing the reference to the block-provided argument, and will be removed.
Edit: An important note! I'm using ARC.
Edit 2: I've noticed that when calling:
[NSFileVersion otherVersionsOfItemAtURL:theURL]
The return value is nil, which indicates (via the documentation):
...or nil if there is no such file. The array does not contain the version object returned by the currentVersionOfItemAtURL: method.
So by checking the return value of this method before I make the call to removeOtherVersionsOfItemAtURL:, it has alleviated the issue. But I still find it strange that an EXC_BAD_ACCESS is thrown, rather than that method handling it properly.
I've noticed that when calling:
[NSFileVersion otherVersionsOfItemAtURL:theURL]
immediately prior to the call to removeOtherVersionsOfItemAtURL:, the return value is nil, which indicates (via the documentation):
Returns: An array of file version objects or nil if there is no such
file. The array does not contain the version object returned by the
currentVersionOfItemAtURL: method.
So by checking the return value of this method before I make the call to removeOtherVersionsOfItemAtURL:, it has alleviated the issue. But I still find it strange that an EXC_BAD_ACCESS is thrown by removeOtherVersionsOfItemAtURL:, rather than that method simply returning NO, or simply populating the provided NSError object.
I'll be filing a Radar and will update here when I hear back.

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}