Skip to content

Commit 744f834

Browse files
author
Alexander Blunck
committed
Working Achievement/Score Caching & Encryption (AppStore save)
1 parent a181927 commit 744f834

File tree

4 files changed

+226
-63
lines changed

4 files changed

+226
-63
lines changed

ABGameKitHelper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
+ (id)sharedClass;
1818

1919
-(void) authenticatePlayer;
20-
-(void) showLeaderboard;
2120
-(void) showAchievements;
21+
-(void) showLeaderboard:(NSString*)leaderboardID;
2222
-(void) reportScore:(int)score forLeaderboard:(NSString*)leaderboardName;
2323
-(void) reportAchievement:(NSString*)identifier percentComplete:(float)percent;
2424

ABGameKitHelper.m

Lines changed: 130 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
#import "ABGameKitHelper.h"
1010
#import "AppDelegate.h"
11+
#import "NSData+AES256.h"
1112

12-
#define APPNAME @"AppName"
13+
#define APPNAME @"MyAppName"
14+
#define AESKEY @"RandomKeyHere"
1315

1416
@implementation ABGameKitHelper
1517

@@ -42,9 +44,12 @@ -(void) authenticatePlayer {
4244
}];
4345
}
4446

45-
-(void) showLeaderboard {
47+
-(void) showLeaderboard:(NSString*)leaderboardID {
4648
GKLeaderboardViewController *leaderboardViewController = [[GKLeaderboardViewController alloc] init];
4749
leaderboardViewController.leaderboardDelegate = self;
50+
if (leaderboardID) {
51+
leaderboardViewController.category = leaderboardID;
52+
}
4853

4954
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
5055
[[app navController] presentModalViewController:leaderboardViewController animated:YES];
@@ -73,54 +78,62 @@ -(void) reportScore:(int)score forLeaderboard:(NSString*)leaderboardName {
7378
}
7479

7580
-(void) cacheScore:(GKScore*)score {
76-
//Retrieve Array of all Cached Scoress
81+
//Retrieve Array of all Cached Achievements
7782
NSData *loadedArrayData = [self loadDataForKey:@"cachedscores"];
78-
NSMutableArray *scoresArray = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
83+
id achievements = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
84+
NSMutableArray *scoresArray = [[NSMutableArray alloc] initWithArray:achievements];
7985

8086
//Add new achievement to Array
8187
[scoresArray addObject:score];
8288

8389
//Save Array back to presitent storage
8490
NSData *newArrayData = [NSKeyedArchiver archivedDataWithRootObject:scoresArray];
8591
[self saveData:newArrayData withKey:@"cachedscores"];
92+
93+
NSLog(@"Cached Score: %lld for LB:%@", score.value, score.category);
8694
}
8795

8896
-(void) reportCachedScores {
8997
//Retrieve Array of all Cached Achievements
9098
NSData *loadedArrayData = [self loadDataForKey:@"cachedscores"];
91-
NSMutableArray *scoresArray = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
99+
id achievements = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
100+
NSMutableArray *scoresArray = [[NSMutableArray alloc] initWithArray:achievements];
92101

93-
//Array to keep track of successfully reported Achievements
94-
NSMutableArray *deleteArray = [[NSMutableArray alloc] init];
102+
NSLog(@"Number of cached scores: %i", scoresArray.count);
95103

96104
for (GKScore *score in scoresArray) {
97105

98106
[score reportScoreWithCompletionHandler:^(NSError *error) {
99107
if (error != nil) {
100108
NSLog(@"GK - Error during reportCachesScores: - Error: %@", error);
109+
[self cacheScore:score];
101110
} else {
102-
NSLog(@"GK - Score:%@ Leaderboard:%@ reported", score.value, score.category);
103-
//Add to deleteArray
104-
[deleteArray addObject:score];
111+
NSLog(@"GK - CachedScore:%lld Leaderboard:%@ reported", score.value, score.category);
105112
}
106113
}];
107114

108115
}
109116

110-
//Delete successfully reported Achievement Objects from achievementArray
111-
[scoresArray removeObjectsInArray:deleteArray];
117+
[self deleteScoresFromCache];
118+
119+
}
120+
121+
-(void) deleteScoresFromCache {
122+
//Retrieve Array of all Cached Achievements
123+
NSData *loadedArrayData = [self loadDataForKey:@"cachedscores"];
124+
id scores = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
125+
NSMutableArray *scoresArray = [[NSMutableArray alloc] initWithArray:scores];
126+
127+
[scoresArray removeAllObjects];
112128

113129
//Save Array back to presitent storage
114130
NSData *newArrayData = [NSKeyedArchiver archivedDataWithRootObject:scoresArray];
115131
[self saveData:newArrayData withKey:@"cachedscores"];
116-
117-
[deleteArray release];
118-
deleteArray = nil;
119132
}
120133

121134
-(void) reportAchievement:(NSString*)identifier percentComplete:(float)percent {
122135

123-
GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:identifier ] autorelease];
136+
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:identifier];
124137
if (achievement) {
125138
achievement.percentComplete = percent;
126139

@@ -144,54 +157,69 @@ -(void) reportAchievement:(NSString*)identifier percentComplete:(float)percent {
144157
}
145158

146159
-(void) cacheAchievement:(GKAchievement*)achievement {
160+
NSLog(@"Cached Achievement: %@", achievement.identifier);
161+
162+
NSString *identifier = achievement.identifier;
163+
double percentage = achievement.percentComplete;
164+
147165
//Retrieve Array of all Cached Achievements
148-
NSData *loadedArrayData = [self loadDataForKey:@"cachedachievements"];
149-
NSMutableArray *achievementArray = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
166+
NSData *loadedDicData = [self loadDataForKey:@"cachedachievements"];
167+
id achievements = [NSKeyedUnarchiver unarchiveObjectWithData:loadedDicData];
168+
NSMutableDictionary *achievementDic = [[NSMutableDictionary alloc] initWithDictionary:achievements];
150169

151170
//Add new achievement to Array
152-
[achievementArray addObject:achievement];
171+
[achievementDic setObject:[NSNumber numberWithDouble:percentage] forKey:identifier];
153172

154173
//Save Array back to presitent storage
155-
NSData *newArrayData = [NSKeyedArchiver archivedDataWithRootObject:achievementArray];
156-
[self saveData:newArrayData withKey:@"cachedachievements"];
174+
NSData *newDicData = [NSKeyedArchiver archivedDataWithRootObject:achievementDic];
175+
176+
[self saveData:newDicData withKey:@"cachedachievements"];
157177
}
158178

159179
-(void) reportCachedAchievements {
160180
//Retrieve Array of all Cached Achievements
161-
NSData *loadedArrayData = [self loadDataForKey:@"cachedachievements"];
162-
NSMutableArray *achievementArray = [NSKeyedUnarchiver unarchiveObjectWithData:loadedArrayData];
163-
164-
//Array to keep track of successfully reported Achievements
165-
NSMutableArray *deleteArray = [[NSMutableArray alloc] init];
166-
167-
for (GKAchievement *achievement in achievementArray) {
168-
if (achievement) {
169-
[achievement reportAchievementWithCompletionHandler:^(NSError *error) {
170-
if (error != nil) {
171-
NSLog(@"GK - Error during reportCachedAchievement: - Error: %@", error);
172-
173-
} else {
174-
NSLog(@"GK - CachedAchievement:%@ Percent:%f reported", achievement.identifier, achievement.percentComplete);
175-
//Locally report Achievement as completed
176-
if (achievement.percentComplete == 100) {
177-
[self saveBool:YES withKey:achievement.identifier];
178-
}
179-
//Add to deleteArray
180-
[deleteArray addObject:achievement];
181+
NSData *loadedDicData = [self loadDataForKey:@"cachedachievements"];
182+
id achievements = [NSKeyedUnarchiver unarchiveObjectWithData:loadedDicData];
183+
NSMutableDictionary *achievementDic = [[NSMutableDictionary alloc] initWithDictionary:achievements];
184+
185+
NSLog(@"Number of cached Achievements: %i", achievementDic.count);
186+
187+
[achievementDic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
188+
NSString *identifier = key;
189+
double percentage = [obj doubleValue];
190+
191+
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:identifier];
192+
achievement.percentComplete = percentage;
193+
[achievement reportAchievementWithCompletionHandler:^(NSError *error) {
194+
if (error != nil) {
195+
NSLog(@"GK - Error during reportCachedAchievement: - Error: %@", error);
196+
197+
} else {
198+
NSLog(@"GK - CachedAchievement:%@ Percent:%f reported", identifier, percentage);
199+
//Locally report Achievement as completed
200+
if (achievement.percentComplete == 100) {
201+
[self saveBool:YES withKey:identifier];
181202
}
182-
}];
183-
}
184-
}
203+
//Add to deleteArray
204+
[self deleteAchievementFromCache:identifier];
205+
}
206+
207+
}];
208+
}];
209+
}
210+
211+
-(void) deleteAchievementFromCache:(NSString*)identifier {
212+
NSData *loadedDicData = [self loadDataForKey:@"cachedachievements"];
213+
id achievements = [NSKeyedUnarchiver unarchiveObjectWithData:loadedDicData];
214+
NSMutableDictionary *achievementDic = [[NSMutableDictionary alloc] initWithDictionary:achievements];
185215

186-
//Delete successfully reported Achievement Objects from achievementArray
187-
[achievementArray removeObjectsInArray:deleteArray];
216+
[achievementDic removeObjectForKey:identifier];
188217

189-
//Save Array back to presitent storage
190-
NSData *newArrayData = [NSKeyedArchiver archivedDataWithRootObject:achievementArray];
191-
[self saveData:newArrayData withKey:@"cachedachievements"];
218+
NSLog(@"post deletion count: %i", achievementDic.count);
192219

193-
[deleteArray release];
194-
deleteArray = nil;
220+
//Save Array back to presitent storage
221+
NSData *newDicData = [NSKeyedArchiver archivedDataWithRootObject:achievementDic];
222+
[self saveData:newDicData withKey:@"cachedachievements"];
195223
}
196224

197225
-(void) resetAchievements {
@@ -224,33 +252,58 @@ -(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewCo
224252

225253
#pragma mark Data Persistence Methods
226254

227-
-(NSString*) getPath {
255+
-(NSString*) getBinaryPath {
256+
257+
NSString *returnString;
258+
228259
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
229260
NSString *documentsDirectory = [paths objectAtIndex:0];
230-
NSString *fileName = [NSString stringWithFormat:@"%@_ABGameKitHelper.plist", APPNAME];
231-
NSString *path = [documentsDirectory stringByAppendingPathComponent:fileName];
232-
return path;
261+
NSString *fullFileName = [NSString stringWithFormat:@"%@_ABGameKitHelper.absave", APPNAME];
262+
NSString *path = [documentsDirectory stringByAppendingPathComponent:fullFileName];
263+
returnString = path;
264+
265+
//Shouldn't happen:
266+
return returnString;
233267
}
234268

235269
-(void) saveData:(NSData *)data withKey:(NSString *)key {
236270
//Check if file exits, if so init Dictionary with it's content, otherwise allocate new one
237-
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self getPath]];
271+
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self getBinaryPath]];
238272
NSMutableDictionary *tempDic;
239273
if (fileExists == NO) {
240274
tempDic = [[NSMutableDictionary alloc] init];
241275
} else {
242-
tempDic = [[NSMutableDictionary alloc] initWithContentsOfFile:[self getPath]];
276+
NSData *binaryFile = [NSData dataWithContentsOfFile:[self getBinaryPath]];
277+
NSData *dataKey = [[NSString stringWithString:AESKEY] dataUsingEncoding:NSUTF8StringEncoding];
278+
NSData *decryptedData = [binaryFile decryptedWithKey:dataKey];
279+
tempDic = [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
243280
}
281+
244282
//Populate Dictionary with to save value/key and write to file
245283
[tempDic setObject:data forKey:key];
246-
[tempDic writeToFile:[self getPath] atomically:YES];
284+
//[tempDic writeToFile:[self getPath:fileName] atomically:YES];
285+
286+
NSData *dicData = [NSKeyedArchiver archivedDataWithRootObject:tempDic];
287+
288+
NSData *dataKey = [[NSString stringWithString:AESKEY] dataUsingEncoding:NSUTF8StringEncoding];
289+
NSData *encryptedData = [dicData encryptedWithKey:dataKey];
290+
291+
[encryptedData writeToFile:[self getBinaryPath] atomically:YES];
292+
247293
//Release allocated Dictionary
248-
[tempDic release];
294+
//[tempDic release];
249295
}
250296

251297
-(NSData*) loadDataForKey:(NSString*)key {
252-
NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithContentsOfFile:[self getPath]];
298+
NSData *binaryFile = [NSData dataWithContentsOfFile:[self getBinaryPath]];
299+
NSData *dataKey = [[NSString stringWithString:AESKEY] dataUsingEncoding:NSUTF8StringEncoding];
300+
NSData *decryptedData = [binaryFile decryptedWithKey:dataKey];
301+
302+
NSMutableDictionary *tempDic = [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
253303
NSData *loadedData = [tempDic objectForKey:key];
304+
305+
//[tempDic release];
306+
254307
return loadedData;
255308
}
256309

@@ -271,8 +324,23 @@ -(BOOL) loadBoolForKey:(NSString*) key {
271324
return [boolean boolValue];
272325
}
273326

274-
275-
327+
-(void) saveINT:(int) number withKey:(NSString*) key{
328+
NSNumber *numberObject = [NSNumber numberWithInt:number];
329+
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:numberObject];
330+
[self saveData:data withKey:key];
331+
}
332+
-(int) loadINTForKey:(NSString*) key{
333+
NSData *loadedData = [self loadDataForKey:key];
334+
NSNumber *loadedNumberObject;
335+
if (loadedData != NULL) {
336+
loadedNumberObject = [NSKeyedUnarchiver unarchiveObjectWithData:loadedData];
337+
} else {
338+
loadedNumberObject = [NSNumber numberWithInt:0];
339+
}
340+
//Convert NSNumber object back to int
341+
int loadedNumber = (int) [loadedNumberObject intValue];
342+
return loadedNumber;
343+
}
276344

277345
@end
278346

NSData+AES256.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// NSData+AES256.h
3+
// Pastry Panic
4+
//
5+
// Created by Alexander Blunck on 08.05.12.
6+
// Copyright (c) 2012 Ablfx. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface NSData (AES256)
12+
13+
- (NSData*) encryptedWithKey:(NSData*) key;
14+
15+
- (NSData*) decryptedWithKey:(NSData*) key;
16+
17+
@end

0 commit comments

Comments
 (0)