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.
Related
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);
}
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.
I am still fairly new in the ReactiveCocoa world and I just wanted to get this common scenario clarified. I noticed that other people are struggling with this matter on GitHub and SO, but I am still missing a proper answer.
The following example does work, but I saw that Justin Summers says that subscriptions-within-subscriptions or subscriptions in general could be code smell. Therefor I want to try and avoid bad habits when learning this new paradigm.
So, the example (using MVVM) is pretty simple:
A ViewController contains a login button which is connected to a login command in the viewmodel
The ViewModel specifies the command action and simulates some network request for this example.
The ViewController subscribes to the command's executingSignals and is able to differentiate the three types of returns: next, error and complete.
And the code.
1 (ViewController):
RAC(self.loginButton, rac_command) = RACObserve(self, viewModel.loginCommand);
2 (ViewModel):
self.loginCommand = [[RACCommand alloc] initWithEnabled:canLoginSignal
signalBlock:^RACSignal *(id input) {
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
BOOL success = [username isEqualToString:#"user"] && [password isEqualToString:#"password"];
// Doesn't really make any sense to use sendNext here, but lets include it to test whether we can handle it in our viewmodel or viewcontroller
[subscriber sendNext:#"test"];
if (success)
{
[subscriber sendCompleted];
} else {
[subscriber sendError:nil];
}
// Cannot cancel request
return nil;
}] materialize];
}];
3 (ViewController):
[self.viewModel.loginCommand.executionSignals subscribeNext:^(RACSignal *execution) {
[[execution dematerialize] subscribeNext:^(id value) {
NSLog(#"Value: %#", value);
} error:^(NSError *error) {
NSLog(#"Error: %#", error);
} completed:^{
NSLog(#"Completed");
}];
}];
How would you do this in a more ReactiveCococa-kind-a-way?
With the way RACCommand works, values come from the executionSignals signal, errors from the errors signal, and completions, well, those are where one might use -materialize and -dematerialize as in your example.
In the example given, login, it arguably don't require completion to model it. Instead a login signal could be defined to be binary in behavior: it either sends #YES (for example), or sends an error. Under these conditions, the code would be:
[[self.viewModel.loginCommand.executionSignals concat] subscribeNext:^(id _) {
// Handle successful login
}];
[self.viewModel.loginCommand.errors subscribeNext:^(NSError *error) {
// Handle failed login
}];
This is obviously a bit of a divergence from the typical subscribeNext:error:completed: pattern typical in RAC. That's just due to RACCommand's API.
Note that the -concat operator has been applied to executionSignals in order to surface the inner values and avoid inner subscriptions. You might also see -flatten or -switchToLatest used in other RACCommand examples, but whenever a command has its allowsConcurrentExecution property set to NO (which is the default), then execution happens serially, making -concat the operator that naturally matches and expresses those serial semantics. Applying -flatten or -switchToLatest would actually work, since they degenerate to -concat when applied to serial signal-of-signals, but they express semantics to the reader that don't apply.
My game implements a custom user interface that lists the local player's friends.
I also have a Game Center leaderboard.
When my game lists the players, it also tries to load their scores from the leaderboard, using this code:
GKLeaderboard *request = [[GKLeaderboard alloc] initWithPlayerIDs:myFriends];
request.timeScope = GKLeaderboardTimeScopeAllTime;
request.identifier = #"my_leaderboards";
if (request != nil) {
[request loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil) {
NSLog(#"Error: %#",error.localizedDescription);
}
if (scores != nil) {
NSLog(#"WORKED: %#",scores);
}
}];
}
And it works just fine.
... except when one of the friends has no score (for instance, they never played the game in the first place). When one of the players in myFriends has no score entry in the leaderboard, the completion handler is never called. There is no error and no score reported, because it never fires in the first place.
I realised this when testing an account that has two friends. One friend has played the game (so they have a score), and the other has not. The completion handler never got called. Then, I unfriended the guy that had no score, and the completion handler worked fine, returning the score of the friend that did have a score.
I somewhat understand this behaviour - after all, I'm asking it to give me a score that does not exist. But is there a workaround? As in, tell it to return a 0 if there is no score?
iOS 7.
It cannot be helped, the completion handler indeed won't be called because there is no score entry in the leaderboards. Although I still don't understand why doesn't it just return an error saying so.
Since the custom friend list just shows statistics, I just put a loading icon in place for each statistic. If the handler is not called for more than 10 seconds, I assume that there is no score entry and just display a 0.
I'm wondering if there is a simple a way to retrieve the best score and the last achievement that has been unlocked for the local user in an iOS app.
Thanks for your advices !
Yes there is:
1) Best score: Create a GKLeaderBoard instance called myLeaderBoard and set the category you want (as in leader board ID) and set the timeScope property to GKLeaderboardTimeScopeAllTime
Then, GKLeaderBoard has the following:
- (void)loadScoresWithCompletionHandler:(void (^)(NSArray *scores, NSError *error))completionHandler
When the request is done, the completion handler is called and from that moment on, myLeaderBoard instance will have the following property: localPlayerScore which is the best score up to now, store it and use it as you wish :)
2) Most recent Achievement: This works in the same spirit, do the following:
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
for(GKAchievement *ach in achievements) {
//sort using the lastReportedDate property of GKAchievement (which is an NSDate).
}
}];