iOS looping error - ios

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.

Related

removeTracksFromPlaylist not removing tracks with ios spotify sdk

I was testing this method to remove tracks from a playlist. Basically I modified the demo project "simple track playback" provided with the SDK. I wanted to remove the track form the playlist when you hit fastForward. I changed the fastForward method this way but it's not doing anything, and error is nil.
-(IBAction)fastForward:(id)sender {
if([self.player isPlaying] && self.currentPlaylistSnapshot){
SPTAuth *auth = [SPTAuth defaultInstance];
[self.currentPlaylistSnapshot removeTracksFromPlaylist:#[self.player.currentTrackURI]
withAccessToken:auth.session.accessToken
callback:^(NSError *error) {
if (error != nil) {
NSLog(#"*** Failed to remove track : %#", self.titleLabel.text);
return;
}
}];
}
[self.player skipNext:nil];
}
self.currentPlaylistSnapshot is the one I've got from the handleNewSession method.
There's also a static method apparently offering something similar which I have't tried yet.
createRequestForRemovingTracks:fromPlaylist:withAccessToken:snapshot:error:
According to the documentation both options are implemented asynchronously and will take seconds to reflect the results in the server but I'm suspecting that there's either something wrong or I'm just missing to do an actual request to push the changes on the local snapshot maybe?
Documentation:
https://developer.spotify.com/ios-sdk-docs/Documents/Classes/SPTPlaylistSnapshot.html#//api/name/removeTracksWithPositionsFromPlaylist:withAccessToken:callback:
ios sdk:
https://github.com/spotify/ios-sdk
I solved my issue by reseting simulator + adding SPTAuthPlaylistModifyPublicScope (which I fogot to do...)
auth.requestedScopes = #[SPTAuthStreamingScope, SPTAuthPlaylistModifyPublicScope];

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.

Why is EDAMNote.content is null?

I want to get the content of the first note in the notebook. I set a content and title. Title is ok but content attribute is null.
EDAMNoteFilter *filter = [[EDAMNoteFilter alloc] init];
[[EvernoteNoteStore noteStore] findNotesWithFilter:filter
offset:0
maxNotes:10
success:^(EDAMNoteList *result) {
if(result.totalNotes>0)
{
EDAMNote *note=result.notes[0];
NSLog(#"%#",[note title]);
NSLog(#"%#",[note content]);
}
}
failure:^(NSError *error) {
// FIXME:zxx 2012-9-26 Alert user error occurs
NSLog(#"Error occurs when retreiving notes: %#", error);
}];
If you read the documentation for the findNotesWithFilter method in the Evernote SDK you will see the following -
Discussion
Used to find a set of the notes from a user’s account based on various
criteria specified via a NoteFilter object.
The Notes (and any embedded Resources) will have empty Data bodies for contents, resource data, and resource recognition fields. These values must be retrieved individually.
You need to retrieve the content using a method such as getNoteWithGuid:withContent:withResourcesData:withResourcesRecognition:withResourcesAlternateData:success:failure:

Game Center leaderboard score request's completion handler is never called for players that have no score

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.

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