I've ran into a problem, I'm currently using GKLeaderboards and using it to fill a model. I'm having no problems fetching the data, my problem occurs when I go to fill the tableView with the data, and it's not done filling the array before its called to fill the TableView. From what i've read i need to use Grand Central Dispatch so that its not loading on the main thread.
Any help would be much appreciated.
+(EILeaderBoardModel *)scoresAndNameFromLeaderBoard
{
static EILeaderBoardModel *leaderBoard = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
leaderBoard = [[EILeaderBoardModel alloc] init];
leaderBoard.highScorePlayerArray = [[self class] GameCenterLeaderBoard];
});
return leaderBoard;
}
+ (NSMutableArray *)GameCenterLeaderBoard
{
NSMutableArray *_highScorePlayer = [NSMutableArray new];
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal;
leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardRequest.range = NSMakeRange(1, 20);
leaderboardRequest.identifier = GameHighscoreIdentifier;
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil)
{
// Handle the error.
}
if (scores != nil)
{
for (NSUInteger i = 0; i < scores.count; i++) {
GKScore *score = (GKScore *)scores[i];
[GKPlayer loadPlayersForIdentifiers:#[score.playerID] withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *player = (GKPlayer *)players[0];
[player loadPhotoForSize:GKPhotoSizeSmall withCompletionHandler:^(UIImage *photo, NSError *error) {
if (error != nil) {
}
UIImage *_avatar;
if (photo != nil) {
_avatar = photo;
} else {
_avatar = [UIImage imageNamed:#"unknownPersonImage.png"];
}
EIPlayer *currentPlayer = [EIPlayer nameLabel:player.displayName
scoreLabel:[NSString stringWithFormat:#"%llD",score.value]
avatar:_avatar];
[_highScorePlayer addObject:currentPlayer];
}];
}];
}
}
}];
return _highScorePlayer;
}
dispatch_queue_t queue = dispatch_queue_create("com.yourQueue.company", 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
// Do all your data fetching work here
// also like adding values to arrays.
dispatch_async(main, ^{
// Do all your UI update logic here like updating data into tables.
[self.tableView reloadData];
});
});
hope this helps.
Related
I am implementing MailCore2 to fetch user's email in my app. I have successfully fetched the email of the user, code is as follows : -
- (void)loadAccountWithUsername:(NSString *)username
password:(NSString *)password
hostname:(NSString *)hostname
oauth2Token:(NSString *)oauth2Token
{
self.imapSession = [[MCOIMAPSession alloc] init];
self.imapSession.hostname = hostname;
self.imapSession.port = 993;
self.imapSession.username = username;
self.imapSession.password = password;
if (oauth2Token != nil) {
self.imapSession.OAuth2Token = oauth2Token;
self.imapSession.authType = MCOAuthTypeXOAuth2;
}
self.imapSession.connectionType = MCOConnectionTypeTLS;
MasterViewController * __weak weakSelf = self;
self.imapSession.connectionLogger = ^(void * connectionID, MCOConnectionLogType type, NSData * data) {
#synchronized(weakSelf) {
if (type != MCOConnectionLogTypeSentPrivate) {
// NSLog(#"event logged:%p %i withData: %#", connectionID, type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
}
};
// Reset the inbox
self.messages = nil;
self.totalNumberOfInboxMessages = -1;
self.isLoading = NO;
self.messagePreviews = [NSMutableDictionary dictionary];
[self.tableView reloadData];
NSLog(#"checking account");
self.imapCheckOp = [self.imapSession checkAccountOperation];
[self.imapCheckOp start:^(NSError *error) {
MasterViewController *strongSelf = weakSelf;
NSLog(#"finished checking account.");
if (error == nil) {
[strongSelf loadLastNMessages:NUMBER_OF_MESSAGES_TO_LOAD];
} else {
NSLog(#"error loading account: %#", error);
}
strongSelf.imapCheckOp = nil;
}];
}
- (void)loadLastNMessages:(NSUInteger)nMessages
{
self.isLoading = YES;
MCOIMAPMessagesRequestKind requestKind = (MCOIMAPMessagesRequestKind)
(MCOIMAPMessagesRequestKindHeaders | MCOIMAPMessagesRequestKindStructure |
MCOIMAPMessagesRequestKindInternalDate | MCOIMAPMessagesRequestKindHeaderSubject |
MCOIMAPMessagesRequestKindFlags);
NSString *inboxFolder = #"INBOX";
MCOIMAPFolderInfoOperation *inboxFolderInfo = [self.imapSession folderInfoOperation:inboxFolder];
[inboxFolderInfo start:^(NSError *error, MCOIMAPFolderInfo *info)
{
BOOL totalNumberOfMessagesDidChange =
self.totalNumberOfInboxMessages != [info messageCount];
self.totalNumberOfInboxMessages = [info messageCount];
NSUInteger numberOfMessagesToLoad =
MIN(self.totalNumberOfInboxMessages, nMessages);
if (numberOfMessagesToLoad == 0)
{
self.isLoading = NO;
return;
}
MCORange fetchRange;
// If total number of messages did not change since last fetch,
// assume nothing was deleted since our last fetch and just
// fetch what we don't have
if (!totalNumberOfMessagesDidChange && self.messages.count)
{
numberOfMessagesToLoad -= self.messages.count;
fetchRange =
MCORangeMake(self.totalNumberOfInboxMessages -
self.messages.count -
(numberOfMessagesToLoad - 1),
(numberOfMessagesToLoad - 1));
}
// Else just fetch the last N messages
else
{
fetchRange =
MCORangeMake(self.totalNumberOfInboxMessages -
(numberOfMessagesToLoad - 1),
(numberOfMessagesToLoad - 1));
}
self.imapMessagesFetchOp =
[self.imapSession fetchMessagesByNumberOperationWithFolder:inboxFolder
requestKind:requestKind
numbers:
[MCOIndexSet indexSetWithRange:fetchRange]];
[self.imapMessagesFetchOp setProgress:^(unsigned int progress) {
NSLog(#"Progress: %u of %u", progress, numberOfMessagesToLoad);
}];
__weak MasterViewController *weakSelf = self;
[self.imapMessagesFetchOp start:
^(NSError *error, NSArray *messages, MCOIndexSet *vanishedMessages)
{
MasterViewController *strongSelf = weakSelf;
NSLog(#"fetched all messages.");
self.isLoading = NO;
NSSortDescriptor *sort =
[NSSortDescriptor sortDescriptorWithKey:#"header.date" ascending:NO];
NSMutableArray *combinedMessages =
[NSMutableArray arrayWithArray:messages];
[combinedMessages addObjectsFromArray:strongSelf.messages];
strongSelf.messages =
[combinedMessages sortedArrayUsingDescriptors:#[sort]];
[strongSelf.tableView reloadData];
}];
}];
}
Now I want to integrate search email functionality in my app. Is there any method available in mailcore2 framework to fetch the searched email with keyword.
Already tried with the link but it was not helpful.
iOS-Is there any method available for fetching CTCoreMessage with specific key words from CTCoreAccount or CTCoreFolder in Mailcore library?
I'm using JSQMessage and am having a little difficulty with showing the placeholder for media until I have it correctly downloading, and then replacing with the media. I have everything working correctly as far as adding the messages and media to server, I just can't get it to replace the placeholders.
Currently, I have a function that queries my database and pulls an array of objects for messages and then loops through and calls this function for each object to output and add it to my message thread. I'm struggling to figure out why the section with "messageToAdd.isMediaMessage" is not replacing the placeholders with the actual media following it's download from the server. Does anyone know how I should be handling this to make sure it adds the message with a placeholder, and then replaces once the media is downloaded correctly?
- (void)addMessage:(PFObject *)object
{
id<JSQMessageMediaData> messageMedia = nil;
PFObject *user = object[#"messageSender"];
[users addObject:user];
NSString *name = #"";
if(user[#"profileFName"] && user[#"profileLName"])
name= [NSString stringWithFormat:#"%# %#",user[#"profileFName"],user[#"profileLName"]];
else
name= [NSString stringWithFormat:#"%# %#",user[#"consultantFName"],user[#"consultantLName"]];
if([object[#"messageFileType"] isEqual: #"video"]){
JSQVideoMediaItem *messageMedia = [[JSQVideoMediaItem alloc] init];
messageMedia.fileURL = nil;
messageMedia.isReadyToPlay = NO;
messageToAdd = [JSQMessage messageWithSenderId:user.objectId displayName:name media:messageMedia];
} else if ([object[#"messageFileType"] isEqual: #"image"]){
JSQPhotoMediaItem *messageMedia = [[JSQPhotoMediaItem alloc] init];
messageMedia.image = nil;
messageToAdd = [JSQMessage messageWithSenderId:user.objectId displayName:name media:messageMedia];
} else{
messageToAdd= [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object[#"sendDate"] text:object[#"messageContent"]];
}
if(isLoadMore)
[messages insertObject:messageToAdd atIndex:0];
else
[messages addObject:messageToAdd];
// NOT TRIGGERING THESE AFTER MEDIA DOWNLOADED
if (messageToAdd.isMediaMessage) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
if ([object[#"messageFileType"] isEqual: #"image"]){
[object[#"messageMedia"] getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] initWithImage:[UIImage imageWithData:imageData]];
((JSQPhotoMediaItem *)messageMedia).image = [UIImage imageWithCGImage:photoItem.image.CGImage];
[self.collectionView reloadData];
}
}];
}
else if([object[#"messageFileType"] isEqual: #"video"]){
PFFile *videoFile = object[#"messageMedia"];
NSURL *videoURL = [NSURL URLWithString:videoFile.url];
((JSQVideoMediaItem *)messageMedia).fileURL = videoURL;
((JSQVideoMediaItem *)messageMedia).isReadyToPlay = YES;
[self.collectionView reloadData];
}
else {
NSLog(#"%s error: unrecognized media item", __PRETTY_FUNCTION__);
}
});
}
}
For others who come along with the same issue/question, I resolved how it was working by looking at the project NotificationChat here:https://github.com/relatedcode/NotificationChat/blob/master/NotificationChat/Classes/Chat/ChatView.m. It gives a really good overview of using the JSQMessage platform.
Here's my modified function so you can see the finished product.
- (void)addMessage:(PFObject *)object
{
PFObject *user = object[#"messageSender"];
[users addObject:user];
PFFile *mediaMessage = object[#"messageMedia"];
NSString *name = #"";
if(user[#"profileFName"] && user[#"profileLName"])
name= [NSString stringWithFormat:#"%# %#",user[#"profileFName"],user[#"profileLName"]];
else
name= [NSString stringWithFormat:#"%# %#",user[#"consultantFName"],user[#"consultantLName"]];
if([object[#"messageFileType"] isEqual: #"video"]){
JSQVideoMediaItem *mediaItem = [[JSQVideoMediaItem alloc] initWithFileURL:[NSURL URLWithString:mediaMessage.url] isReadyToPlay:YES];
mediaItem.appliesMediaViewMaskAsOutgoing = [user.objectId isEqualToString:self.senderId];
messageToAdd = [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object.createdAt media:mediaItem];
} else if ([object[#"messageFileType"] isEqual: #"image"]){
JSQPhotoMediaItem *mediaItem = [[JSQPhotoMediaItem alloc] initWithImage:nil];
mediaItem.appliesMediaViewMaskAsOutgoing = [user.objectId isEqualToString:self.senderId];
messageToAdd = [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object.createdAt media:mediaItem];
[mediaMessage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
if (error == nil)
{
mediaItem.image = [UIImage imageWithData:imageData];
[self.collectionView reloadData];
}
}];
} else{
messageToAdd= [[JSQMessage alloc] initWithSenderId:user.objectId senderDisplayName:name date:object[#"sendDate"] text:object[#"messageContent"]];
}
if(isLoadMore)
[messages insertObject:messageToAdd atIndex:0];
else
[messages addObject:messageToAdd];
}
Based on the code I think one possible reason is you need reloadData on main(UI) thread after download data successfully and asynchronously on background thread
I have downloaded images and saved it to a GCD, updated the count of images and performed a post notification in queue. What is happening now is that it does not register that I have downloaded the images. Sometimes I am missing some images, and I think it is because I have missed something in my GCD logic.
Here is my code:
for (NSString *i in items)
{
[[RequestAPI sharedInstance]downloadImage:i completion:^(AFHTTPRequestOperation *operation, UIImage *image, NSError *error) {
//1. here main thread I receive images and go to BG
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//2. here I save image on disk and get path
NSString *path = [ImageManager saveImageToDisk:image toEntity:entity withparams:#{#"save" : #"lala"}];
__block NSMutableDictionary *attachments = [NSMutableDictionary dictionary];
__block NSMutableArray *photoPaths = [NSMutableArray array];
dispatch_async(dispatch_get_main_queue(), ^{
//3. here I load entity and dictionary from it with NSKeyedUnarchiver from CD and set to it image path
if (entity.attachments)
{
attachments = [NSKeyedUnarchiver unarchiveObjectWithData:entity.attachments];
if (attachments[type])
{
photoPaths = attachments[type];
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//4. here I check all images equality ti themselves in entity
BOOL haveDublicate = NO;
NSData *i = [ImageManager imageDataFromPath:path];
NSArray *photoImages = [ImageManager imageDatasFromPaths:photoPaths];
for (NSData *saved in photoImages)
{
if ([saved isEqualToData: i])
{
haveDublicate = YES;
}
}
if (!photoPaths)
{
photoPaths = [NSMutableArray array];
}
dispatch_async(dispatch_get_main_queue(), ^{
//5. and finally if all ok I save image path, change load counter and post notification
if (path.length
&& ![photoPaths containsObject:path]
&& !haveDublicate
)
{
[photoPaths addObject:path];
[savedLinks setObject:photoPaths forKey:type];
entity.attachments = [NSKeyedArchiver archivedDataWithRootObject:savedLinks];
[self saveContext];
}
[RequestAPI sharedInstance].downloadsCount -= 1;
[[NSNotificationCenter defaultCenter]postNotificationName:kReloadFeedData object:nil];
});
});
});
});
}];
I think here I need process 1-2-3-4-5 to get desired result. Am I right or how can I do this queuing?
Below is the method with the code, where I am getting a memory leak using manual memory management. The memory leak is detected by using Xcode instruments and specifically points to the line where I am using NSJSONSerialization. I am running the target app (on a device with iOS 6.1).
The first time that i tap on the refreshButton there is no leak. Any subsequent tap generates the leak(and more leaks on top of that if i continue tapping the button). Below is the code - This is basic stuff for consuming JSON web services(the web service link is bogus but the real one that I am using works). You will notice that I am using Grand Central Dispatch so that I can update the UI without waiting for the parsing of the JSON to finish.
The line detected by instruments is surrounded by the asterisks. I would like to get some help to anyone who might have an idea of what is going on here. The full stack trace(as mentioned in the below comments i will put here:)
+(NSJSONSerialization JSONObjectWithData:option:error:] -> -[_NSJSONReader parseData:options:] -> -[_NSJSONReader parseUTF8JSONData:skipBytes:options]->newJSONValue->newJSONString->[NSPlaceholderString
initWithBytes:length:encoding:]
-(void)parseDictionary:(NSDictionary *)dictonary{
self.transactions = [dictonary objectForKey:#"transactions"];
if(!self.transactions){
NSLog(#"Expected 'transactions' array");
return;
}
for (int arrayIndex = 0; arrayIndex < [self.transactions count]; arrayIndex++) {
TransactionResult *result = [[[TransactionResult alloc] init] autorelease];
result.transactionID = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"ID"];
result.transactionDescription = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"description"];
result.transactionPrice = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"price"];
self.totalPrice += [result.transactionPrice doubleValue];
NSLog(#"total price: %f", self.totalPrice);
[self.transactionResults addObject:result];
result = nil;
}
}
- (IBAction)refreshButtonPressed:(UIBarButtonItem *)sender {
__block id resultObject;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:#"http://mywebservice.php"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
***resultObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];***
if(!error){
if([resultObject isKindOfClass:[NSDictionary class]]){
NSDictionary *dictonary = resultObject;
[self parseDictionary:dictonary];
NSLog(#"Done parsing!");
dispatch_async(dispatch_get_main_queue(), ^{
self.isLoading = NO;
[self.transactionsTableView reloadData];
});
}
else{
NSLog(#"JSON Error: Expected Dictionary");
resultObject = nil;
return;
}
}
else{
NSLog(#"JSON Error: %#", [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
resultObject = nil;
[self.transactionsTableView reloadData];
[self showError];
});
return;
}
});
}
I used a ARC as soon as it came out with 4.3, and put an app in the store with it - point being you could switch to ARC. That said, I tried to reproduce your problem by creating a class/file that has the no-arc flag applied to it, but cannot reproduce the problem. This makes me believe your problem is elsewhere. In the code below, I create a Test object in another file, retain it, and send it the test message. No matter what I set "i" to, it always deallocs the object:
#import "Tester.h"
#interface Obj : NSObject <NSObject>
#end
#implementation Obj
- (id)retain
{
NSLog(#"retain");
id i = [super retain];
return i;
}
- (oneway void)release
{
NSLog(#"release");
[super release];
}
- (void)foo
{
}
- (void)dealloc
{
NSLog(#"Obj dealloced");
[super dealloc];
}
#end
#implementation Tester
- (void)test
{
int i = 2;
__block Obj *obj;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
obj = [[Obj new] autorelease];
if(i == 0) {
Obj *o = obj;
dispatch_async(dispatch_get_main_queue(), ^
{
[o foo];
} );
} else if(i == 1) {
obj = nil;
} else if(i == 2) {
dispatch_async(dispatch_get_main_queue(), ^
{
obj = nil;
} );
}
} );
}
#end
So I was wondering after reading the apple docs(https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLeaderboard_Ref/Reference/Reference.html#//apple_ref/occ/instp/GKLeaderboard/category) how would one create a UITableView and fill it with the localPlayers Game Center friends and there scores in a specific leaderboard. I know how to get the friends list and friends scores individually by using the loadScoresWithCompletionHandler: method.
Edit: So far I got this to get individual friends photo, score and displayname saved into one NSArray. But i can't figure out how to disply them in a UITableView.
- (void) loadPlayerData: (NSArray *) identifiers
{
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
if (leaderboardRequest != nil) {
leaderboardRequest.playerScope = GKLeaderboardPlayerScopeFriendsOnly;
leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardRequest.category = #"MJ_IL";
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil) {
// handle the error. if (scores != nil)
}
if (scores != nil){
for (GKScore* score in scores) {
NSArray *playerIdArray = [NSArray arrayWithObject:score.playerID];
[GKPlayer loadPlayersForIdentifiers:playerIdArray withCompletionHandler:^(NSArray *players, NSError *error) {
GKPlayer *player = [players objectAtIndex:0];
[player loadPhotoForSize:GKPhotoSizeSmall withCompletionHandler:^(UIImage *photo, NSError *error) {
if (score.playerID == player.playerID) {
if (photo != nil) {
playerInfo = [NSArray arrayWithObjects:score, player.displayName, photo, nil];
} else if (photo == nil) {
playerInfo = [NSArray arrayWithObjects:score, player.displayName, nil];
}
if (error != nil) {
NSLog(#"%#", error.localizedDescription);
}
}
}];
}];
}
}
}];
}
}
- (void)compareLocalPlayerScoreWithFriends {
GKScore *friendScore = [playerInfo objectAtIndex:0];
NSString *friendDisplayName = [playerInfo objectAtIndex:1];
if ([playerInfo objectAtIndex:2] != nil) {
UIImage *friendPhoto = [playerInfo objectAtIndex:2];
if (friendScore.value > interactiveHighscore) {
[friendNameLabel setText:friendDisplayName];
[friendScoreLabel setText:(NSString *)friendScore];
friendImageView.image = friendPhoto;
}
}
}
Thanks guys,
Georges
Take a look at this tutorial by Ray Wenderlich, it explains how to display simple pictures and text in a UITableView - there are three parts and should get you working with, at least, a basic but working view.
At its very core level this is the code that does "the work" for displaying in a UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"MyBasicCell"];
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
cell.textLabel.text = bug.data.title;
cell.imageView.image = bug.thumbImage;
return cell;
}
Update
Here is my code for generating leaderbord data with alias and photos, hope you can modify it appropriately but shouldnt be too different
-(void)getScoresAndAliasForLeaderboard:(GKLeaderboard *)leaderboardRequest{
if (leaderboardRequest == nil)
{
leaderboardRequest = [[GKLeaderboard alloc] init];
leaderboardRequest.playerScope = GKLeaderboardPlayerScopeFriendsOnly;
leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardRequest.category = #"HighScore";
leaderboardRequest.range = NSMakeRange(1,100);
}
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil)
{
// Handle the error.
}
if (scores != nil)
{
NSMutableArray *retrievePlayerIDs = [[NSMutableArray alloc] init];
for (GKScore *s in scores)
{
[retrievePlayerIDs addObject:s.playerID];
GCLeaderboardScore *playerScore = [[GCLeaderboardScore alloc] init];
playerScore->playerID = s.playerID;
playerScore->score = (int)s.value;
playerScore->rank = s.rank;
playerScores[s.playerID] = playerScore; //playerScores is a NSMutableDictionary
if ([s.playerID isEqualToString: leaderboardRequest.localPlayerScore.playerID]){
me = playerScore;
}
}
if (me == nil){
me = [[GCLeaderboardScore alloc] init];
me->playerID = leaderboardRequest.localPlayerScore.playerID;
me->score = leaderboardRequest.localPlayerScore.value;
me->alias = #"Me";
playerScores[me->playerID] = me;
}
[GKPlayer loadPlayersForIdentifiers:retrievePlayerIDs withCompletionHandler:^(NSArray *playerArray, NSError *error)
{
for (GKPlayer* p in playerArray)
{
GCLeaderboardScore *playerScore = playerScores[p.playerID];
playerScore->alias = p.alias;
[p loadPhotoForSize:GKPhotoSizeSmall withCompletionHandler:^(UIImage *photo, NSError *error) {
if (photo != nil) {
playerScore->photo = photo;
}
else{
playerScore->photo = [UIImage imageNamed:#"wordpress_avatar.jpg"];
}
if (error != nil) {
NSLog(#"%#", error.localizedDescription);
}
}];
}
}];
}
}];
}