iOS Convert Facebook Graph JSON result to NSMutableArray - ios

I use
if ([FBSDKAccessToken currentAccessToken]) {
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/v2.3/116178611870507/feed?limit=2" parameters:[NSMutableDictionary dictionaryWithObject:#"id, message, picture" forKey:#"fields"]]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSLog(#"fetched user:%#", result);
}
else {
NSLog(#"%#", error);
}
}];
}
and get:
{
data = (
{
"created_time" = "2015-08-08T13:19:59+0000";
id = "FirstID";
message = "First Message";
},
{
"created_time" = "2015-08-07T13:34:20+0000";
id = "THE ID";
message = "Message Content";
}
);
I would like to get this into an NSMutableArray to allow for easier creation of a tableview, using this data. How can I go about converting it, as it contains a lot of non UTF-8 such as \u2019?
Here is the non-edited result code I get for the open graph request. As you can see in it, there are numerous non UTF-8 elements such as /u2019 instead of '. I just need to put this into an array, while converting all those elements.:
{
data = (
{
"created_time" = "2015-08-08T13:19:59+0000";
id = "116178611870507_533557810132583";
message = "10 HABITS OF HAPPY COUPLES\n\nWhat does it take to be happy in a relationship? If you\U2019re working to improve your marriage, here are the 10 habits of happy couples.\n\n1. GO TO BED AT THE SAME TIME. Remember the beginning of your relationship, when you couldn\U2019t wait to go to bed with each other to make love? Happy couples resist the temptation to go to bed at different times. They go to bed at the same time, even if one partner wakes up later to do things while their partner sleeps. And when their skins touch it still causes each of them to tingle and unless one or both are completely exhausted to feel sexually excited.\n\n2. CULTIVATE COMMON INTERESTS. After the passion settles down, it\U2019s common to realize that you have few interests in common. But don\U2019t minimize the importance of activities you can do together that you both enjoy. If common interests are not present, happy couples develop them. At the same time, be sure to cultivate interests of your own; this will make you more interesting to your mate and prevent you from appearing too dependent.\n\n3. WALK HAND IN HAND OR SIDE BY SIDE. Do things together. Rather than one partner lagging or dragging behind the other, happy couples walk comfortably hand in hand or side by side. They know it\U2019s more important to be with their spouse than to see the sights along the way.\n\n4. MAKE TRUST AND FORGIVENESS YOUR DEFAULT MODE. If and when they have a disagreement or argument, and if they can\U2019t resolve it, happy couples default to trusting and forgiving rather than distrusting and begrudging.\n\n5. FOCUS MORE ON WHAT YOUR SPOUSE DOES RIGHT THAN WHAT HE OR SHE DOES WRONG. If you look for things your partner does wrong, you can always find something. If you look for what he or she does right, you can always find something, too. It all depends on what you want to look for. Happy couples accentuate the positive.\n\n6. HUG EACH OTHER AS SOON AS YOU SEE EACH OTHER AFTER WORK. Our skin has a memory of \U201cgood touch\U201d (loved), \U201cbad touch\U201d (abused) and \U201cno touch\U201d (neglected). Couples who say hello with a hug keep their skin bathed in the \U201cgood touch,\U201d which can inoculate your spirit against anonymity in the world.\n\n7. SAY \"I LOVE YOU\" AND \"HAVE A GOOD DAY\" EVERY MORNING. This is a great way to buy some patience and tolerance as each partner sets out each day to battle traffic jams, long lines and other annoyances.\n\n8. SAY \"GOOD NIGHT\" EVERY NIGHT, REGARDLESS OF HOW YOU FEEL. This tells your partner that, regardless of how upset you are with him or her, you still want to be in the relationship. It says that what you and your partner have is bigger than any single upsetting incident.\n\n9. DO A \"WEATHER\" CHECK DURING THE DAY. Call your spouse at home or at work to see how his or her day is going. This is a great way to adjust expectations so that you\U2019re more in sync when you connect after work. For instance, if your partner is having an awful day, it might be unreasonable to expect him or her to be enthusiastic about something good that happened to you.\n\n10. BE PROUD TO BE SEEN WITH YOUR SPOUSE. Happy couples are pleased to be seen together and are often in some kind of affectionate contact \U2014 hand on hand or hand on shoulder or knee or back of neck. They are not showing off but rather just saying that they belong with each other.\n\nHappy couples have different habits than unhappy couples. A habit is a discrete behavior that you do automatically and that takes little effort to maintain. It takes 21 days of daily repetition of a new a behavior to become a habit. So select one of the behaviors in the list above to do for 21 days and voila, it will become a habit\U2026and make you happier as a couple. And if you fall off the wagon, don\U2019t despair, just apologize to your partner, ask their forgiveness and recommit yourself to getting back in the habit. ~ Dr. Mark Goulson";
},
{
"created_time" = "2015-08-07T13:34:20+0000";
id = "116178611870507_533189820169382";
message = "31 DUMB WAYS TO KILL YOUR MARRIAGE: \n\nThere are probably hundreds of dumb things to do in your marriage, but here are 31 that will destroy your marriage\U2026\n\n1. Believe that your marriage will be great without having to invest time or effort into it.\n\n2. NEVER compliment your spouse.\n\n3. Stop pursuing your spouse like you did before marriage.\n\n4. Be critical all the time.\n\n5. Demand that your spouse meets your needs.\n\n6. Pout when you don\U2019t get your way.\n\n7. Consider sex a chore and only do it because you feel obligated.\n\n8. Use the silent treatment when you are mad.\n\n9. Get angry \U2026 often.\n\n10. Flirt with people other than your spouse.\n\n11. Tell your spouse they need to make changes, but you make no effort to change.\n\n12. Never be willing to meet your spouse\U2019s needs.\n\n13. Make no effort to improve yourself or your marriage.\n\n14. Speak negatively about your spouse around other people.\n\n15. Make no effort to keep yourself \U201clooking good\U201d for your spouse.\n\n16. Use derogatory words like stupid, dumb, ignorant and idiot.\n\n17. Always blame your spouse for the problems. It\U2019s never your fault.\n\n18. Take your mate for granted.\n\n19. Quit trying to impress your spouse (you\U2019re married, it\U2019s not needed anymore).\n\n20. Withhold sex.\n\n21. Never take any time to get away without the kids.\n\n22. Don\U2019t tell your spouse how much you love or appreciate them. Just expect them to know it.\n\n23. Be dishonest.\n\n24. When arguing \U2026 bring up old stuff from the past.\n\n25. Have a short fuse.\n\n26. Feel that God is an option in your marriage, not an essential.\n\n27. When angry be verbally abusive.\n\n28. Never seek outside help when you have problems.\n\n29. Have an \U201cit\U2019s my way or the highway\U201d attitude.\n\n30. Spend more time with your friends than your spouse.\n\n31. Never say, \U201cI love you.";
}
);
Here is my full code. Since there are 2 posts, I expect to have 2 objects in my NSMutableArray, and for each object in the array, use the RSSEntry class to sort through and create a new NSMutableArray from that. However, I only get 1 item in the NSMutableArray I create.
if ([FBSDKAccessToken currentAccessToken]) {
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/v2.3/116178611870507/feed?limit=2" parameters:[NSMutableDictionary dictionaryWithObject:#"id, message, picture" forKey:#"fields"]]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
// NSLog(#"fetched user:%#", result);
self.arrayName = [[NSMutableArray alloc] init];
[self.arrayName addObject:[result objectForKey:#"data"]];
// NSLog(#"%#", self.arrayName);
int i;
NSLog(#"%lu", (unsigned long)[self.arrayName count]);
for (i = 0; i < [self.arrayName count]; i++) {
id myArrayElement = [self.arrayName objectAtIndex:i];
NSString *theMessage = [myArrayElement valueForKey:#"message"];
// NSLog(#"%#", theMessage);
RSSEntry *entry = [[[RSSEntry alloc] initWithBlogTitle:#"Test"
articleTitle:#"Testing"
articleUrl:#"TestingURL"
articleDate:[NSDate date]
articleImage:theMessage] autorelease];
[self.theEntries addObject:entry];
}
NSLog(#"%#", self.theEntries);
}
else {
NSLog(#"%#", error);
}
}];
}

The problem is these two lines:
self.arrayName = [[NSMutableArray alloc] init];
[self.arrayName addObject:[result objectForKey:#"data"]];
You create an array and then you add an array to that one ([result objectForKey:#"data"] returns an array).
Replace those two lines with:
self.arrayName = [result[#"data"] mutableCopy];
Now self.arrayName will contain an array with the two desired dictionaries.
You can also improve your code by using more modern syntax:
if (!error) {
// NSLog(#"fetched user:%#", result);
self.arrayName = [result[#"data"] mutableCopy];
// NSLog(#"%#", self.arrayName);
NSLog(#"%lu", (unsigned long)self.arrayName.count);
for (NSDictionary *myArrayElement in self.arrayName) {
NSString *theMessage = myArrayElement[#"message"];
// NSLog(#"%#", theMessage);
RSSEntry *entry = [[[RSSEntry alloc] initWithBlogTitle:#"Test"
articleTitle:#"Testing"
articleUrl:#"TestingURL"
articleDate:[NSDate date]
articleImage:theMessage] autorelease];
[self.theEntries addObject:entry];
}
NSLog(#"%#", self.theEntries);
} else {
NSLog(#"%#", error);
}

Related

iOS looping error

So I'm having a weird problem with loops in while developing in iOS. There are over hundreds of lines of code, so instead of boring you with it, I'm going to describe the problem.
I am trying to upload events from Facebook to an Microsoft Azure server. To do so, I made a class called FacebookUploader. In that class, I loop through the events of Facebook (and set all the variables to the class. ie. the name of the first event is self.event, when loops continues and goes to the second event, that events name is self.event now). What I expected iOS to do was to go through each event in the loop, upload it, and then move onto the next one.
This worked while I was just uploading the event. Now, I'm also first checking the database to see if the event is already there. And then when I get confirmation that it's not there, I upload it. However, when it comes time to upload it, Facebook Uploader has already finished looping through all the events and only the last event gets uploaded (multiple times, one for each event that should be uploaded).
I've pinpointed the error, now I'm asking, what am I doing wrong? Should the variable not be associated with the class and instead be local? Or is there a way to stop running the loop until we get confirmation from the server that the event is not there? Your help is much appreciated.
MSQuery *query = [self.table queryWithPredicate: [NSPredicate predicateWithFormat:#"fb_id == %#", self.id]];
[query readWithCompletion:^(MSQueryResult *result, NSError *error) {
//not in table, add it
NSArray *items = [result.items mutableCopy];
if (items == nil || [items count] == 0) {
NSDictionary *newItem;
if(self.end_time != nil) {
newItem = #{#"fb_id": self.id, #"event_name": self.name, #"event_location": self.location, #"event_start": self.start_time, #"event_end": self.end_time, #"event_description": self.descriptionOfEvent, #"picture_url": self.picture_url, #"event_latitude": self.venue_latitude, #"event_longitude": self.venue_longitude, #"popularity": self.popularity};
}
else {
newItem = #{#"fb_id": self.id, #"event_name": self.name, #"event_location": self.location, #"event_start": self.start_time, #"event_description": self.descriptionOfEvent, #"picture_url": self.picture_url, #"event_latitude": self.venue_latitude, #"event_longitude": self.venue_longitude, #"popularity": self.popularity};
}
[self.table insert:newItem completion:^(NSDictionary *result, NSError *error) {
// The result contains the new item that was inserted,
// depending on your server scripts it may have additional or modified
// data compared to what was passed to the server.
if(error) {
NSLog(#"ERROR %#", error);
} else {
NSLog(#"Todo Item: %#", [result objectForKey:#"event_name"]);
}
}];
}
The answer to the question was given by Paulw11 above. By allocating the variables outside of the loop, I was creating multiple copies of the variables, causing my error. Allocating the variable inside of the loop fixed the error.

Firebase how to check transaction success or fail?

I am trying to update a firebase node in transaction, simple stuff. Followed the doc:
https://www.firebase.com/docs/ios/guide/saving-data.html
Firebase* upvotesRef = [[Firebase alloc] initWithUrl: #"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes"];
[upvotesRef runTransactionBlock:^FTransactionResult *(FMutableData *currentData) {
NSNumber *value = currentData.value;
if (currentData.value == [NSNull null]) {
value = 0;
}
[currentData setValue:[NSNumber numberWithInt:(1 + [value intValue])]];
return [FTransactionResult successWithValue:currentData];
}];
The big question is:
How do I check the outcome of this transaction(success/fail)? I wish to make some UI changes depends on the outcome of it.
There is another method in SDK document appears to have a callback, but it did not explain which value should I check against. And it says something about the method could be run multiple times. How do I make sure when it gives the "final" result?
https://www.firebase.com/docs/ios-api/Classes/Firebase.html#//api/name/runTransactionBlock:andCompletionBlock:
Sorry I am such a beginner that apple style doc really doesn't come together without some examples.
If you just want to wait for the final value, runTransactionBlock:andCompletionBlock: is the method you want to look at. Here's some example code:
[upvotesRef runTransactionBlock:^FTransactionResult *(FMutableData *currentData) {
NSNumber *value = currentData.value;
if (currentData.value == [NSNull null]) {
value = 0;
}
[currentData setValue:[NSNumber numberWithInt:(1 + [value intValue])]];
return [FTransactionResult successWithValue:currentData];
} andCompletionBlock:^(NSError *error, BOOL committed, FDataSnapshot *snapshot) {
if (error) {
NSLog(#"Error: %#", error);
}
if (committed) {
NSLog(#"Data committed");
}
NSLog(#"Final Value: %#", snapshot.value);
}];
That last value there, snapshot.value is where you can get your final value. It's the same sort of FDataSnapshot that you get if you use observeEventType:withBlock:
If something goes wrong, you'll get an error.
If your data is committed, committed will be YES. If you returned [FTransactionResult abort] in your transaction block instead of [FTransactionResult successWithValue:], committed will be NO.
This means, if you read the counter at 4 and try to update it. You might try to update the counter at the same time someone else does. If yours get in first, snapshot.value will be 5. If the other person's update gets in before you do, the snapshot.value will be 6.
You probably wanted to get up to 6 no matter who upvoted first. To do this, you need to add an observer. The code for that might look like:
[upvotesRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"New Value: %#", snapshot.value);
}];
With this, you don't need the completion block to find out the final value because every time the transaction blocks, the observer block will fire. In the example scenario above, it would fire once for 5 and once for 6 no matter who upvoted first. You do need the completion block if you want to find out whether or not your particular transaction succeeded and not just what value is at that location now.
And, just to be complete, there is one more method called runTransactionBlock:andCompletionBlock:withLocalEvents:. If others are also writing to the same location, the transaction block may run multiple times. This happens if it finds out that it is running on stale data. When it runs successfully on fresh data, it will call the completion block. However, you'll find that each time it runs, it will fire any observer blocks at that location. If you don't want this to happen, you should pass NO to withLocalEvents:. Your Firebase will trigger events at that location whenever a confirmed write goes through, but your local transaction's temporary writes, which are unconfirmed, will not.
Looking back to the example where you and another person are trying to upvote at the same time. By default, the observer will fire as soon as you try to update the count from 4 to 5. The actual transaction might fail because someone else pushed the upvote count from 4 to 5 at the same time. Your transaction block would then run again with the new data, 5, and see that it should push the count to 6. With local events set to NO, the observer will fire after the server lets you know someone else pushed the upvote count from 4 to 5, and not when you try to update the count from 4 to 5.
This isn't a huge deal with something simple like upvotes where everyone is just incrementing, but if you could potentially be pushing different data from other users, any observers at the location may see the data jump around before finally settling.

reset a single gamecenter achievement

In my app I have an achievement for 10 wins in a row. So when the user wins 5 games in a row I report the achievement 50% completed - this works fine. When the user loses some games I call my resetAchievment method which sets the percentage to 0 and reports the percentage again. However when I restart the app the percentage gets read from the GKAchivement and it still shows 50%.
- ( void ) resetAchievement
{
_gamekitAchievement.percentComplete=0.0f;
_counter = 0;
[self report];
}
- ( void ) report
{
_gamekitAchievement.showsCompletionBanner = YES;
[_gamekitAchievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error)
{
NSLog(#"reporting Achievment: %# failed, error: %#", _gamekitAchievement.identifier, [error localizedDescription]);
}
}];
}
Is it not possible to report a smaller percentage again - or am I doing something wrong?
I have no actual experience from GameKit at all but from reading the documentation and searching the web it seems you're only able to report progress and not regression(?) Not to mention the fact that you can only reset ALL achievements... Perhaps the following would still help you achieve (ahem) what you want:
Update all your local achievement data with + (void)loadAchievementsWithCompletionHandler:
Store this data in the userDefaults for safety
Do NOT reset the local achievement data
Reset all the achievements with + (void)resetAchievementsWithCompletionHandler:
Change the percentComplete to 0 on the achievement in question.
report progress of all the achievements from your local storage
Now, as I mention, having no hands-on experience with this framework the above might not be practical for a number of reasons I am unaware of (the way progress is presented to the player for instance). Guess it was worth a shot to share the idea anyways...
Short answer: It is not possible to report a smaller percentage again.
Long answer: A lower GameCenter score will not overwrite a higher one.
For example you have a high score of 10 points and it's recorded in GameCenter.
If in the next try you get 5 points and they are submitted to GC, your high score of 10 won't be affected.
Similarly if you try to 'reset' your score, it counts as submitting a new score of 0, so it won't make a difference.
This only applies to scores and achievements submitted to GameCenter, inside your app you can do anything you want (for example your own custom leaderboard or something)
If you think about it, 5 wins is not 50% of the way there, since they could lose the 6th, then win 10 in a row, in which case they played 16 total games and 5 wins was more like 30% of the way there. In this case, it is all or nothing, so don't report anything until they have all 10.
Please be aware that storing the progress towards an achievement in NSUserDefaults doesn't take different GameCenter users into account. That means, if Player A (logged into GameCenter) makes some progress and at 90 % progress another Player B on the same device logs into GameCenter with his own account, he starts with the progress made by Player A.
You might solve this by storing a whole NSDictionary with the Player IDs as keys into NSUserDefaults.
What you'll want to do is save it NSUserdefaults. then, Call the NSUser default and check what number it is by using an IF statement.
-(void)checkAcheivement{
scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
if(scoreNumber == 10){
//Run code to save achievement in Game Center(_gamekit stuff)
}
//-(void)checkScoreFire{
//scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
//if(scoreNumber == 5){
//Run code to set up alert view for 5 wins below
//UIAlertView* message = [[UIAlertView alloc]
// initWithTitle: #"Alert"
// message: #"You are halfway to the Achievement!"
// delegate: self
// cancelButtonTitle: #"Dismiss"
// [message show];
//}
//to save score use this method:
-(void)saveScore{
scoreNumber = [NSUserDefaults standardUserDefaults] objectForKey:#"GamesWon"]
if(gameScore > scoreNumber){
[[NSUserDefaults standardUserDefaults]setInteger:scoreNumber forKey:#"GamesWon"];
}
This will only post the achievement if it is ten, i suggest not putting the achievement in until then, because it makes it more difficult. I also suggest setting up an AlertView to let the person know they are halfway there. You can do so with the code commented above with the checkScoreFive method. Remember to declare everything in the .h file as well.
IBOutlet UIAlertView *AlertView;
-(void)checkAcheivement;
-(void)checkScoreFire;
-(void)saveScore;
int gameScore;
int scoreNumber;

Error using semaphore token to run code after a group of asynchronous web service calls

I am in a situation where I need to call multiple web service requests at a time to call to my server to delete messages on the server and I am having a difficult time trying to figure out the best way to trigger some methods to refresh my data at the completion of these group of web service calls.
From what I have researched using a semaphore counter should work for what I am wanting to do, but I am running into a problem where upon calling dispatch_release() on my semaphore token my application crashes with this -
libdispatch.dylib`_dispatch_semaphore_dispose$VARIANT$mp:
0x3c03ed70: push {r7, lr}
0x3c03ed72: ldr r1, [r0, #40]
0x3c03ed74: mov r7, sp
0x3c03ed76: ldr r2, [r0, #36]
0x3c03ed78: cmp r1, r2
0x3c03ed7a: bge 0x3c03ed7e ; _dispatch_semaphore_dispose$VARIANT$mp + 14
0x3c03ed7c: trap
Everything i've found on this problem points to the semaphore token being referenced by something but I can't see what would have a reference to the token.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
long messagesCount = [messages count] - 1;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(messagesCount);
BOOL (^isLastRequest)(void) = ^BOOL (void) {
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
if (0 == result) {
return false;
}
dispatch_release(semaphore);
return true;
};
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
if (error) {
isLastRequest();
completion(NO);
} else {
if (isLastRequest()) {
completion(YES);
}
}
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
EDIT
Thanks for the great answers. As Dustin said I was attempting to use dispatch_semaphore for something that it should not be used for. I accepted his answer because it was simple to implement and didn't need any re-structure of what i'm doing currently to send my web services. I now have some good reading material though about dispatch_groups in general though!
Thanks for all your help!
I'm not sure this is the most efficient way to solve your problem, but just looking through your code, one problem you might be running into is that you're creating the semaphore with a count and trying to release it when that count is less than the initial count. In GCD, always create a semaphore with 0 and then signal the semaphore to the correct number of "resources" you need to count. The reason is because semaphores can't be destroyed/released if their count is less than the initial count. It kinda makes sense if you think of it as a resource counter. Having a semaphore number less than the initial count means that you have a worker still using one of the resources.
You can see this code here http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c. The code that will throw the exception in _dispatch_semaphore_dispose is:
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
This is exactly what dispatch_group is designed to address. You dispatch several blocks to a group, and when they have all completed, another block will be executed (or you can wait on them if you need a synchronous behavior).
First see Waiting on Groups of Queued Tasks in the Concurrency Programming Guide, and see the dispatch_group functions in the GCD reference. To see them in action, see the JuliaCell example from Chapter 13 of iOS:PTL. Cocoa Samurai also has some examples.
Even if you can't actually dispatch the blocks to a group (it may not work with how MSMClient operates), you can still use dispatch groups manually by calling dispatch_group_enter() and dispatch_group_leave() to get the same behavior you're trying to get from the semaphore.
As a side note, BOOL is not the same as bool. BOOL return YES and NO, which are different than true and false (which I assume means you're compiling this as ObjC++, which always makes me shudder, but that's a different issue). Mixing them can matter because they can be (and sometimes are) different sizes. I've had to crashes due to that personally.
This is an incorrect usage for dispatch_semaphore -- it isn't meant to do what you are attempting. What you need is a counting variable that is thread safe. You can get this using __sync_sub_and_fetch.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
__block long messagesCount = [messages count];
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
long which = __sync_sub_and_fetch(&messageCount, 1);
if(which == 0)
completion(error == nil);
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
__sync_sub_and_fetch tells the CPU that you want it to take latest version of 'messageCount' from all threads (and cores), subtract 1, and give you the result.

GameCenter skinning

Is it possible to take data we get from GameCenter and create our own skin for it?
If so where can I access all the data we receive? The important data I want is current matches, everything else is not a big deal to me. Can anyone help?!
These should get you going:
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(void (^)(NSArray *matches, NSError *error))completionHandler];
[GKTurnBasedMatch loadMatchDataWithCompletionHandler:(void (^)(NSData *matchData, NSError *error))completionHandler];
Edit:
It would take a very long post to explain the whole process step by step, but here is the main idea:
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(void (^)(NSArray *matches, NSError *error)){
for (GKTurnBasedMatch *myMatch in matches) {
// update your UI depending on the games. Below is just an example.. This part is up to you - update a tableView, manage a view etc..
int k = 0; // will hold the number of active players still in the game
for (GKTurnBasedParticipant *part in myMatch.participants) {
if(participant.matchOutcome != GKTurnBasedMatchOutcomeQuit){
k++;
}
}
if ([myMatch.currentParticipant.playerID isEqualToString [GKPlayer localPlayer].playerID]) {
//our turn
if (k<2) { //there are less than 2 active players - end game if it's your turn etc...
//end turn depending on your turn.
return;
}
//update your UI for that match..
} else { //not your turn
//update your UI - goes to their turn section for example
}
}
}];
Again I just wrote all of this from the top of my head so I'm sure there are mistakes but thats the main route you want to take. You want to get a list of your current matches - and list them according to whose turn it is or if the game is ended and other things.

Resources