Parse Swift Custom Object Id? - ios

I would like to create an object with a custom Object Id. The reason I want to do this is because I would like the save method to fail if it tries to create a row with the same data. For example: Parse automatically fails to save if you try to sign up with an email that is already taken. I would like the same thing to happen for data in a class that is not a User class.
Alternatively I could make things work if I knew how to do this
if(class contains column with "this string of data"){
do nothing}
else{
save "this string of data"}
What I'm doing is implementing an up-voting and down-voting system, and I don't want users to be able to vote more than once on a single post.
Each time a user votes, it would enter into parse a row of data with a column that is of string type that would be content of post + voter. and so if that combination of content of post + voter would try to be saved again, I want it to fail.
This is the code for a cell in the tableview. This is the downVote code. (the upVote code would be very similar)
//intVotes goes into a label with the amount of votes
//thisReview contains the content of the post
//downVote() increments number of votes by -1 in Parse
#IBAction func downVote(sender: AnyObject) {
var reviewQuery: PFQuery = PFQuery(className: "reviews")
reviewQuery.whereKey("content", equalTo: reviewTextView.text)
reviewQuery.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
let review:PFObject = object as! PFObject
self.defaults.setValue(review["content"], forKey: "thisReview")
}
}
}
var vote:PFObject = PFObject(className: "votes")
if String(stringInterpolationSegment: vote.valueForKey("votes")) != String(stringInterpolationSegment: defaults.valueForKey("thisReview")) + String(stringInterpolationSegment: PFUser.currentUser()){
vote.setValue(String(stringInterpolationSegment: defaults.valueForKey("thisReview")) + String(stringInterpolationSegment: PFUser.currentUser()), forKey: "objectId")
vote.saveInBackgroundWithBlock {
(succeeded: Bool, error: NSError!) -> Void in
if error == nil {
self.downVote()
var intVotes: Int = self.votes.text!.toInt()!
intVotes = intVotes - 1
self.votes.text = "\(intVotes)"
} else {
println ("Failed")
}
}
}
else{
//do nothing
}
}
This ALMOST works, except the left side of the equation in the if statement (the vote.valueForKey part) returns nil every time.

You should just translate what Parse already shows you in their tutorial named "AnyPic" they implment and up/down voting "like button" system in ObjC, here's the main element translated to Swift you will have to import PAPCache.h/m PAPConstants.h/m and PAPUtility.h/m into your project and build out a bridging header to link to these files. From there, the only part you need to figure out is how to modify the following code to fit your needs, but this is already working, you just need to rearrange the variables, and change stuff to Optionals or NON-optionals to make this work for you. I'd assume that this method is THE BEST method since Parse uses this method themself in their ObjC BIG tutorial to show off everything which is the app "AnyPic", you'll have to declare a property in your UIVIewController of type PFObject and more, but this is the guts of the code. No one is going to give you the full code since this is going to take a lot of code and it would be easier if you did this in Swift instead of needing to do what I just did in the last 30 minutes. Bridging this to match the ObjC code is going to be laborious, but this is how it's done. Good luck!
What you need to declare, at minnimum:
var likeUsers : NSArray?
var likeButton: UIButton?
var someObject: PFObject?
Method:
func didTapLikeButtonAction(button: UIButton) {
var liked = Bool()
liked = !button.selected
button.removeTarget(self, action: "didTapLikeButtonAction(button)", forControlEvents: UIControlEvents.TouchUpInside)
var originalLikeUsersArray = NSArray()
originalLikeUsersArray = self.likeUsers!
var newLikeUsersSet = NSMutableSet(capacity: self.likeUsers!.count)
for id in self.likeUsers! {
if id.objectId != PFUser.currentUser()?.objectId {
newLikeUsersSet.addObject(id)
}
}
if liked {
PAPCache.sharedCache().incrementLikerCountForPhoto(self.someObject)
newLikeUsersSet.addObject(PFUser.currentUser()!)
} else {
PAPCache.sharedCache().decrementLikerCountForPhoto(self.someObject)
}
PAPCache.sharedCache().setPhotoIsLikedByCurrentUser(self.someObject, liked: liked)
likeUsers = newLikeUsersSet.allObjects
if liked {
PAPUtility.likePhotoInBackground(self.someObject, block: {(success: Bool, error: NSError?) -> Void in
if !success {
button.addTarget(self, action: "didTapLikeButtonAction(button)", forControlEvents:UIControlEvents.TouchUpInside)
self.likeUsers = originalLikeUsersArray
self.setLikeButtonState(true)
}
})
} else {
PAPUtility.unlikePhotoInBackground(self.someObject, block: {(success: Bool, error: NSError?) -> Void in
if !success {
button.addTarget(self, action: "didTapLikeButtonAction(button)", forControlEvents:UIControlEvents.TouchUpInside)
self.likeUsers = originalLikeUsersArray
self.setLikeButtonState(false)
}
})
}
}
LikeButtonFunction translated from ObjC
func setLikeButtonState(selected: Bool) {
if selected {
likeButton?.titleEdgeInsets = UIEdgeInsetsMake( -1.0, 0.0, 0.0, 0.0)
} else {
likeButton?.titleEdgeInsets = UIEdgeInsetsMake( 0.0, 0.0, 0.0, 0.0)
}
likeButton?.selected = selected
}
You will need to download the "Anypic" project from here:
https://parse.com/tutorials/anypic
and you will need to import into you Swift project, at minnimum, the following:
#import "PAPCache.h"
#import "PAPConstants.h"
#import "PAPUtility.h"
You will then need to recode the PAPCache, PAPUtility, and PAPConstants to fit your needs. Good luck, this will be a lot of coding due to Swift, but could be close to no coding if you were to use ObjC as Parse has said over and over again that they will not make a big push into Swift until it's battle tested. The last time they said this again was just two months ago in June.
The original code, from Objective-C, there's some things I didn't do for you since this is YOUR app and you will have to do these things yourself if you see it necessary, again, the ObjC code is done, but you chose to use Swift, so recoding what has already been provided basically, "out of the box" is what you are going to have to deal with:
- (void)didTapLikePhotoButtonAction:(UIButton *)button {
BOOL liked = !button.selected;
[button removeTarget:self action:#selector(didTapLikePhotoButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self setLikeButtonState:liked];
NSArray *originalLikeUsersArray = [NSArray arrayWithArray:self.likeUsers];
NSMutableSet *newLikeUsersSet = [NSMutableSet setWithCapacity:[self.likeUsers count]];
for (PFUser *likeUser in self.likeUsers) {
if (![[likeUser objectId] isEqualToString:[[PFUser currentUser] objectId]]) {
[newLikeUsersSet addObject:likeUser];
}
}
if (liked) {
[[PAPCache sharedCache] incrementLikerCountForPhoto:self.photo];
[newLikeUsersSet addObject:[PFUser currentUser]];
} else {
[[PAPCache sharedCache] decrementLikerCountForPhoto:self.photo];
}
[[PAPCache sharedCache] setPhotoIsLikedByCurrentUser:self.photo liked:liked];
[self setLikeUsers:[newLikeUsersSet allObjects]];
if (liked) {
[PAPUtility likePhotoInBackground:self.photo block:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
[button addTarget:self action:#selector(didTapLikePhotoButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self setLikeUsers:originalLikeUsersArray];
[self setLikeButtonState:NO];
}
}];
} else {
[PAPUtility unlikePhotoInBackground:self.photo block:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
[button addTarget:self action:#selector(didTapLikePhotoButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self setLikeUsers:originalLikeUsersArray];
[self setLikeButtonState:YES];
}
}];
}
[[NSNotificationCenter defaultCenter] postNotificationName:PAPPhotoDetailsViewControllerUserLikedUnlikedPhotoNotification object:self.photo userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:liked] forKey:PAPPhotoDetailsViewControllerUserLikedUnlikedPhotoNotificationUserInfoLikedKey]];
}
- (void)didTapLikerButtonAction:(UIButton *)button {
PFUser *user = [self.likeUsers objectAtIndex:button.tag];
if (delegate && [delegate respondsToSelector:#selector(photoDetailsHeaderView:didTapUserButton:user:)]) {
[delegate photoDetailsHeaderView:self didTapUserButton:button user:user];
}
}
- (void)didTapUserNameButtonAction:(UIButton *)button {
if (delegate && [delegate respondsToSelector:#selector(photoDetailsHeaderView:didTapUserButton:user:)]) {
[delegate photoDetailsHeaderView:self didTapUserButton:button user:self.photographer];
}
}
The ObjC code from above comes from the file "PAPPhotoDetailsHeaderView.m" of the Parse.com AnyPic github repo and you can see their OBJECTIVE-C tutorial on this on their web site at the web site I've listed above.
And, by the way, this DOES work for me, and it does compile for me, but I don't use Swift, so this is useless to me, but if you set things up correctly, you don't need to mess around with the PAPCache, PAPConstants, and PAPUtility. But this assumes you are well versed in all things Parse. Anyway, good luck.
I just added the likeButtonOn/Off function, translated from ObjC

Related

NSArray of NSRegularExpressions in swift

The fork here has a custom link function but the implementation in the actual code is in objective C.This is the code line and her it is printed again:
- (NSArray<NSRegularExpression*>*)getRegularExpressions
{
return [NSArray arrayWithObject:[NSRegularExpression regularExpressionWithPattern:#"#([a-zA-Z0-9])+" options:0 error:NULL]];
}
I was wondering how i could reproduce this in swift, I have already placed all of the framework code, I just need to know how to do this.
Try this
func getRegularExpressions() -> [NSRegularExpression] {
var arrayOfExpressions = [NSRegularExpression]()
do
{
let expresion = try NSRegularExpression(pattern: "#([a-zA-Z0-9])+", options:.CaseInsensitive)
arrayOfExpressions.append(expresion)
}catch _
{
arrayOfExpressions.removeAll()
return arrayOfExpressions
}
return arrayOfExpressions;
}
I hope this helps you

Backendless - How To Get Objects From 'Data'

How do I get all the objects from Backendless's database into a UITableView in my iOS app?
Looking at their Documentation, it doesn't clearly state how to get all objects. (I'm new to the platform)
Any help would be appreciated!
Here's how I do it in Swift (for my table of Blurb objects):
func retrieveBlurbs() {
let query = BackendlessDataQuery()
// Use backendless.persistenceService to obtain a ref to a data store for the class
let dataStore = self.backendless.persistenceService.of(Blurb.ofClass()) as IDataStore
dataStore.find(query, response: { (retrievedCollection) -> Void in
print("Successfully retrieved: \(retrievedCollection)")
self.blurbs = retrievedCollection.data as! [Blurb]
self.tableView.reloadData()
}) { (fault) -> Void in
print("Server reported an error: \(fault)")
}
}
I am also new to Backendless and really enjoying it! It's a lot like Parse, but better in a bunch of ways.
Start with this:
https://backendless.com/feature-16-data-retrieval-api-how-to-load-objects-from-an-mbaas-storage/
Then move on to this: https://backendless.com/feature-17-data-paging-or-how-to-efficiently-load-large-data-sets-in-a-mobile-app/
Both articles include concrete examples in Swift.
Try this:
- (void)viewDidLoad {
[super viewDidLoad];
[self getDataFromBackendless];
}
-(void)getDataFromBackendless {
#try {
BackendlessCollection *documents = [backendless.persistenceService of:[YOUR_TABLE_NAME class]];
currentPage =[documents getCurrentPage];
}
#catch (Fault *fault) {
NSLog(#"Server reported an error: %#", fault);
}
}
Then perform UITableView methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [currentPage count];
}

Prevent a closure from running until another has completed

Here is code for two closures in two different IBAction button presses. The desired outcome is for the button press to turn on/off an LED, then to access a light sensor and read the light value after the change in LED status.
What happens is a race condition where the function getVariable runs and returns before the callFunction has implemented the change. The result is that the value displayed in getLightLabel.text is that of the prior condition, not the current condition.
My question is how to rewrite the code below so that myPhoton!.getVariable does not execute until after the myPhoton!.callFunction has returned (completed its task).
I have tried placing getVariable inside callFunction, both before and after the } closing if (error == nil), but the result was identical to the code shown here.
#IBAction func lightOn(sender: AnyObject) {
let funcArgs = [1]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is on"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
#IBAction func lightOff(sender: AnyObject) {
let funcArgs = [0]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is off"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
Here is the callFunction comments and code from the .h file. This SDK is written in Objective C. I am using it in Swift with a bridging header file.
/**
* Call a function on the device
*
* #param functionName Function name
* #param args Array of arguments to pass to the function on the device. Arguments will be converted to string maximum length 63 chars.
* #param completion Completion block will be called when function was invoked on device. First argument of block is the integer return value of the function, second is NSError object in case of an error invoking the function
*/
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion;
/*
-(void)addEventHandler:(NSString *)eventName handler:(void(^)(void))handler;
-(void)removeEventHandler:(NSString *)eventName;
*/
Here is the .m file code
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion
{
// TODO: check function name exists in list
NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:#"v1/devices/%#/%#", self.id, functionName]];
NSMutableDictionary *params = [NSMutableDictionary new]; //[self defaultParams];
// TODO: check response of calling a non existant function
if (args) {
NSMutableArray *argsStr = [[NSMutableArray alloc] initWithCapacity:args.count];
for (id arg in args)
{
[argsStr addObject:[arg description]];
}
NSString *argsValue = [argsStr componentsJoinedByString:#","];
if (argsValue.length > MAX_SPARK_FUNCTION_ARG_LENGTH)
{
// TODO: arrange user error/codes in a list
NSError *err = [self makeErrorWithDescription:[NSString stringWithFormat:#"Maximum argument length cannot exceed %d",MAX_SPARK_FUNCTION_ARG_LENGTH] code:1000];
if (completion)
completion(nil,err);
return;
}
params[#"args"] = argsValue;
}
[self setAuthHeaderWithAccessToken];
[self.manager POST:[url description] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion)
{
NSDictionary *responseDict = responseObject;
if ([responseDict[#"connected"] boolValue]==NO)
{
NSError *err = [self makeErrorWithDescription:#"Device is not connected" code:1001];
completion(nil,err);
}
else
{
// check
NSNumber *result = responseDict[#"return_value"];
completion(result,nil);
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
if (completion)
completion(nil,error);
}];
}
One solution is to put the second closure inside the first, where the first returns and provides and Error value. If no error,then execuet the second closure. That is one way to tightly couple the two closures without resorting to semaphores or other messaging schemes.
In this application, the problem I was encountering cannot be solved on the IOS/Swift side of the stack. The cloud API and embedded uP are not tightly coupled, so the cloud returns to the IOS with a completion before the full function code has run on the Particle uP.
The solution to this overall problem actually lies in either modifying the cloud API or adding some additional code to the uP firmware to tightly couple the process to the IOS app with additional communication.

No response when calling NSURLConnection in mixed Swift Objective-C environment

I've created the Class StartConnection to handle my NSURL requests. It gets called from my AppDelegate twice and that works well. It's called by one other class as well and that also works well. This is the implementation of StartConnection:
#import "StartConnection.h"
#import "BlaBlaBlog-swift.h"
#implementation StartConnection
{
BOOL startedForBlog;
}
- (void)getRssFileWithUrl: (NSString*)rssUrlString forBlog:(BOOL)forBlog
{
startedForBlog = forBlog;
NSURL *url = [NSURL URLWithString:rssUrlString];
NSURLRequest *rssRequest = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:rssRequest delegate:self];
[connection start];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
dataSize = [response expectedContentLength];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (receivedData==nil )
{
receivedData = [[NSMutableData alloc]init];
}
// Append the new data to the instance variable you declared
[receivedData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.receivedDataComplete = receivedData;
if (startedForBlog){
[self.delegate performSelector: #selector(receiveDataCompleted)];
}
else
[self.delegate performSelector: #selector(receivePodCastDataCompleted)];
}
To get some hands on experience with SWIFT I've added an experimental SWIFT class to my code that also uses StartConnection.h. In the debugger I can see an instance of StartConnection being created and the getFileWithUrl methode seems to be kicked of normally. But that's all that happens. None of the delegate methods is called.
This is SWIFT class:
import UIKit
class CheckActuality: NSObject, GetReceivedDataProtocol, WordPressParserDelegate {
var retrievePostData = StartConnection()
var parseCompleted: Bool=false
var result: Bool = true
lazy var wpParser = WordPressParser()
lazy var defaults = NSUserDefaults.standardUserDefaults()
func isActual () -> Bool {
var url = "http://blablablog.nl/new_api.php?function=get_recent_posts&count=1"
self.retrievePostData.delegate=self
self.retrievePostData.getRssFileWithUrl(url, forBlog:true)
while !self.parseCompleted
{
// wait till wpparser has completed
}
if self.wpParser.arrayWithPostDictionaries.count == 1
// Some info has been retrieved
{
var posts: NSArray = wpParser.arrayWithPostDictionaries
var post: NSDictionary = posts.objectAtIndex(0) as NSDictionary
var latestPostUrl: String = post.objectForKey("postUrl") as String
var currentLatestPostUrl = defaults.stringForKey("ttt")
if latestPostUrl != currentLatestPostUrl {
result = false
}
else {
result = true
}
}
return result
}
func receiveDataCompleted () {
if self.retrievePostData.receivedDataComplete != nil
{
self.wpParser.delegate=self
self.wpParser.parseData(retrievePostData.receivedDataComplete)
}
else
{
// warning no internet
}
}
func wpParseCompleted () {
self.parseCompleted=true
}
}
And to be complete, the call in my AppDelegate look like this:
//
// retrieving PostData. Create a connection, set delegate to self en start with created url
//
retrievePostData = [[StartConnection alloc]init];
retrievePostData.delegate = self;
NSString *url = [[wordPressUrl stringByAppendingString:apiString] stringByAppendingString:pageCountString];
[retrievePostData getRssFileWithUrl:url forBlog:(BOOL)true];
//
// retrieving PodcastData. Create a connection, set delegate to self en start with created url
//
retrievePodCastData = [[StartConnection alloc]init];
retrievePodCastData.delegate = self;
[retrievePodCastData getRssFileWithUrl:podcastUrl forBlog:(BOOL)false];
I'm breaking my head for almost a day. Hope some of you far more experienced guys can help this starter out.
The while !parseCompleted loop is blocking the main thread until the download and parsing is done. But the processing of the received data happens on the main thread, too, so if that thread is blocked, your app will be deadlocked.
I would eliminate that while loop and put all of the post processing inside your receivedDataComplete method.
By the way, implicit in this change is the fact that isActual must be an asynchronous method. Thus, rather than returning a Bool value, it should be a Void return type but you should employ the completionHandler pattern (and I'd change it to also return an error object, too):
class CheckActuality: NSObject, GetReceivedDataProtocol, WordPressParserDelegate {
let errorDomain = "com.domain.app.CheckActuality"
lazy var retrievePostData = StartConnection()
lazy var wpParser = WordPressParser()
lazy var defaults = NSUserDefaults.standardUserDefaults()
var completionHandler: ((latestPost: Bool!, error: NSError?)->())!
func isActual(completionHandler: (latestPost: Bool!, error: NSError?)->()) {
// save the completionHandler, which will be called by `receiveDataCompleted` or `wpParseCompleted`
self.completionHandler = completionHandler
// now perform query
let url = "http://blablablog.nl/new_api.php?function=get_recent_posts&count=1" // as an aside, use `let` here
retrievePostData.delegate = self // also note that use of `self` is redundant here
retrievePostData.getRssFileWithUrl(url, forBlog:true)
}
func receiveDataCompleted () {
if retrievePostData.receivedDataComplete != nil {
wpParser.delegate = self
wpParser.parseData(retrievePostData.receivedDataComplete)
} else {
// frankly, I'd rather see you change this `receiveDataCompleted` return the `NSError` from the connection, but in the absence of that, let's send our own error
let error = NSError(domain: errorDomain, code: 1, userInfo: nil)
completionHandler(latestPost: nil, error: error)
}
}
func wpParseCompleted () {
if wpParser.arrayWithPostDictionaries.count == 1 { // Some info has been retrieved
let posts: NSArray = wpParser.arrayWithPostDictionaries
let post: NSDictionary = posts.objectAtIndex(0) as NSDictionary
let latestPost: String = post.objectForKey("postUrl") as String
let currentlatestPost = defaults.stringForKey("ttt")
completionHandler(latestPost: (latestPost != currentlatestPost), error: nil)
}
// again, I'd rather see you return a meaningful error returned by the WordPressParser, but I'll make up an error object for now
let error = NSError(domain: errorDomain, code: 2, userInfo: nil)
completionHandler(latestPost: nil, error: error)
}
}
Now, I don't know if latestPost is the appropriate name for the value you were trying to return, so change that to whatever makes sense for your routine. Also, the name isActual doesn't really make sense, but I'll let you change that to whatever you want.
Anyway, when you use it, you'd use the trailing closure syntax to specify the completionHandler block that will be performed asynchronously:
let checkActuality = CheckActuality()
func someFunc() {
checkActuality.isActual() { latestPost, error in
if error != nil {
// do whatever error handling you want
println(error)
} else if latestPost {
// yes, latest post
} else {
// nope
}
}
// note, do not try to check `latestPost` here because the
// above closure runs asynchronously
}
Needless to say, this is a completionHandler pattern, but looking at your code, you seem to favor delegate patterns. If you wanted to implement this using the delegate pattern, you can. But the idea is the same: This isActual method (whatever you end up renaming it to) runs asynchronously, so you have to inform the caller when it is complete.

Game Center Outcome Posting

I’m doing a turn based game... the only information transferred is the score of the player and whether a turn has been sent.
When the next player receives the turn. The data gets stored into “scoreToBeat” and turnSent=1. The player then takes their turn. Afterward the game ended gets called because turnSent=1. I used the turn based tutorial by Ray Wenderlich at http://www.raywenderlich.com/5480/beginning-turn-based-gaming-with-ios-5-part-1.
In his case the game ends and is a tie. Like this... I can’t seem to get it to show the person that lost.
for (GKTurnBasedParticipant *part in currentMatch.participants) {
part.matchOutcome = GKTurnBasedMatchOutcomeTied;
}
I can’t seem to get it to show the person that lost it always shows a win. This is my latest attempt of many... Exactly 2 players in the match btw... Any ideas would be greatly appreciated.
for (GKTurnBasedParticipant *part in currentMatch.participants) {
if(part==currentMatch.currentParticipant)
{
if(points>scoreToBeat)
{
part.matchOutcome=GKTurnBasedMatchOutcomeWon;
}
else {
part.matchOutcome=GKTurnBasedMatchOutcomeLost;
}
}
else {
if(points>scoreToBeat)
{
part.matchOutcome=GKTurnBasedMatchOutcomeLost;
}
else {
part.matchOutcome=GKTurnBasedMatchOutcomeWon;
}
}
This is an excerpt from my latest project, good for a 2 player game. It's called during the sendTurn process, when the game is decided to be ending. This is more correct than the previous answer (first code block), in my opinion, because you must set the matchOutcome for all participants before ending the game.
If you had more than 2 players, then you'd have to loop through all participants and set the matchOutcome accordingly.
GKTurnBasedParticipant *curr = currentMatch.currentParticipant;
NSUInteger currentIndex = [currentMatch.participants indexOfObject:currentMatch.currentParticipant];
NSUInteger nextIndex = (currentIndex + 1) % [currentMatch.participants count];
GKTurnBasedParticipant *next = [currentMatch.participants objectAtIndex:nextIndex];
if (currScore < otherScore)
{
// Curr player lost
curr.matchOutcome = GKTurnBasedMatchOutcomeLost;
next.matchOutcome = GKTurnBasedMatchOutcomeWon;
}
else if (currScore == otherScore)
{
// Tied
curr.matchOutcome = GKTurnBasedMatchOutcomeTied;
next.matchOutcome = GKTurnBasedMatchOutcomeTied;
}
else
{
// Won
curr.matchOutcome = GKTurnBasedMatchOutcomeWon;
next.matchOutcome = GKTurnBasedMatchOutcomeLost;
}
Note that you will see "Won" on both devices by design.
One will say "Me" with "Won" underneath, and the other will say "(Winners Name)" with "Won" underneath.
points is the local points scored on the device right? if so, I would do the following:
if([part.playerID isEqualToString [GKLocalPlayer localPlayer].playerID]]) {
if(points>scoreToBeat) {
part.matchOutcome = GKTurnBasedMatchOutComeWon;
} else {
part.matchOutcome = GKTurnBasedMatchOutComeLost;
}
} else {
if(points>scoreToBeat) {
part.matchOutcome=GKTurnBasedMatchOutcomeLost;
} else {
part.matchOutcome=GKTurnBasedMatchOutcomeWon;
}
}
}
Also, remember to use a NSLog to see if score to beat is actually transferred. One mistake you can do to keep on using currentTurnBasedMatch.matchData, you should set the currentTurnBasedMatch to GKTurnBasedMatch that is returned in delegate methods.
EDIT: A code snipped I use looks like this
if([participant.playerID isEqualToString:[GKLocalPlayer localPlayer].playerID]){
NSLog(#"we are the last player in the game -end the game");
for (GKTurnBasedParticipant *part in match.participants) {
part.matchOutcome = GKTurnBasedMatchOutcomeTied;
}
[match endMatchInTurnWithMatchData:match.matchData completionHandler:^(NSError *error) {
if ([AppDelegate mainMenuController].multiGameMenu.turnBasedMenu !=nil) {
[[AppDelegate mainMenuController].multiGameMenu.turnBasedMenu reloadTableView];
}
} ];
}

Resources