I am trying to work out the correct way to approach some methodology.
Workflow
When a game is created, I would like to first search to see if a game already exists with this user. If there is a game I will not create one and show a message to the user.
At present I have two methods:
+(void)createNewGameAgainst:(PFUser *)user2 withCompletion:(void (^)(BOOL success))completionHandler
+(BOOL)checkIfGameAlreadyExistsAgainst:(PFUser *)opponentUser
The createNewGame... method is called first. Then within this I make a call to [self checkIfGameAlreadyExistsAgainst:user2];.
How do I check the result of the second method, from within the first? So how do I determine what the BOOL value is of the call to the method checkIfGameAlreadyExistsAgainst?
Is this the correct way to approach this or is there a better/cleaner way possibly?
The return value of a function can be used like a variable:
BOOL gameExists = [self checkIfGameAlreadyExistsAgainst:user2]; // assign result to a new variable
if(gameExists == YES) // compare result to YES
{
}
You can skip creating a new variable and just compare the result
if ([self checkIfGameAlreadyExistsAgainst:user2] == YES) // compare result directly
{
}
And when the type is BOOL, you can omit the comparison and just do this:
if ([self checkIfGameAlreadyExistsAgainst:user2])
{
}
Related
I have an app with a Share button. I want to customize what content is shared based on the activity type. For example, Messages might get an image and text, whereas AirDrop would just get a file.
I actually have this working perfectly, and the code I'm using has worked fine in every version of iOS through iOS 10. But I've realized I'm returning nil where I'm not supposed to, so I'm trying to figure out how to fix that.
I do something like this to set up my activity view controller:
JUNActivityProvider *fileProvider = [[JUNActivityProvider alloc] initWithPlaceholderItem:[NSObject new]];
fileProvider.objectID = objectID;
fileProvider.fileURL = fileURL;
JUNActivityProvider *textProvider = [[JUNActivityProvider alloc] initWithPlaceholderItem:[NSString new]];
textProvider.objectID = objectID;
...
UIActivityViewController *activityController = [[UIActivityViewController alloc]
initWithActivityItems:#[fileProvider,imageProvider,textProvider,urlProvider,printFormatter]
applicationActivities:nil];
Then in JUNActivityProvider, I have an item method that customizes the return value based on the activityType:
- (id)item {
if (self.fileURL) {
if ([self.activityType isEqualToString:UIActivityTypeAirDrop]) {
// Create the file
return url;
}
} else if ([self.placeholderItem isKindOfClass:[UIImage class]]) {
if ([self.activityType isEqualToString:UIActivityTypeAirDrop] == NO &&
[self.activityType isEqualToString:UIActivityTypeMail] == NO &&
[self.activityType isEqualToString:UIActivityTypePrint] == NO) {
// Create the image
return image;
}
} else if ([self.placeholderItem isKindOfClass:[NSString class]]) {
if ([self.activityType isEqualToString:UIActivityTypeMail]) {
return #"example one";
} else if ([self.activityType isEqualToString:UIActivityTypeMessage] ||
[self.activityType isEqualToString:UIActivityTypeCopyToPasteboard]) {
return #"example two";
}
}
return nil;
}
That return return nil at the end is the problem. It works fine and does exactly what I want—when it's nil that item isn't shared. The written documentation doesn't say that it must return a value, but the header file does:
- (nonnull id)item; // called on secondary thread when user selects an activity. you must subclass and return a non-nil value.
I don't want to risk a crash by returning nil when a nonnull value is expected, so I need to fix this. As far as I can tell my only option is to stop using UIActivityItemProvider, and instead implement the UIActivityItemSource protocol on my own. That protocol includes the method activityViewController:itemForActivityType:, which clearly states that you can return nil there:
May be nil if multiple items were registered for a single activity type, so long as one of the items returns an actual value.
Perfect. But here's the problem: activityViewController:itemForActivityType: is called on the main thread, which is causing problems with one of my items in particular. Here's a summary of what's happening:
I need to call some methods that run asynchronously. In order to deal with that I've tried using a dispatch semaphore. That keeps the method from returning until I've had a chance to set the return value.
Since activityViewController:itemForActivityType: is called on the main thread, that locks up while it's working.
I need to draw a UIView into an image. If I try to do that work on the main thread, nothing happens until the semaphore times out. But if I don't do it on the main thread, it crashes.
I'm at a loss for how to deal with this. Basically I need to keep the method from returning until I'm ready, but I can't lock up the main thread since I need to do some work there. This seems… impossible? Is there any way to make this work?
After filing an enhancement request I was just about to give up and settle on either returning nil or [NSNull null]. But then I realized there is absolutely a solution to this problem.
While UIActivityItemProvider includes a bunch of its own functionality, it still very much implements the UIActivityItemSource protocol. I knew that. What I hadn't considered is that this means I can just override activityViewController:itemForActivityType: and return nil there when it's appropriate.
So the last line of my item method now looks like this:
return self.placeholderItem;
You could also return [NSNull null] here, or really any object. I chose the placeholderItem because it seems a little safer—at the very least I know it's returning an object of the expected type, in case anything about the implementation ever changes.
Then all I have to do is add my own implementation of activityViewController:itemForActivityType: (where we are allowed to return nil):
- (nullable id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType {
id item = [super activityViewController:activityViewController itemForActivityType:activityType];
if ([item isEqual:self.placeholderItem]) return nil;
return item;
}
Just call super to get the item, return nil if it's something you don't want to include, or return the item if it is. Note that if your placeholderItem might ever be equal to something you actually do want to share, you will need to change this implementation a bit—but the same basic concept should work.
I have a method which calls itself:
-(void)myMethod
{
//do stuff
[self myMethod];
//do stuff
}
I need to check, from inside myMethod where it is being called from. For example, IF called myMethod do this, ELSE do this.
Can you just pass in a boolean to show called from external vs called from recursion?
-(void)myMethod:(bool)externalCall
{
//do stuff
[self myMethod:false];
//do stuff
}
And then call that from outside with:
[self myMethod:true];
That may be over simplifying, especially if you need to get the calling method from multiple different locations (instead of recursion vs external call), but it seems to me the simplest answer to your presented problem.
Being a ReactiveCocoa newbie, I'm hoping for some advice with this:
I'm trying to create a dynamic form that contains multiple Field objects parsed from an XML file. Each Field can have muliple validation rules that will run against the Field's NSString *value param.
For the RAC part of the question-
inside each Field object, I want to bind BOOL completed to a signal that checks the Field's *value param against an array of rules. So far I've gotten here with my thinking:
#implementation Field
self = [super init];
if (self) {
RAC(self, completed) = [RACObserve(self, value) filter:^BOOL(NSString *fieldValue) {
NSLog(#"%s::self.completed = %d\n", sel_getName(_cmd), self.completed); // trying to watch the values here, with no luck
NSLog(#"%s::fieldValue = %#\n", sel_getName(_cmd), fieldValue); // same here, I'd like to be able to view the `*value` here but so far no luck
return [self validateCurrentValue]; // currently this method just checks value.length > 5
}];
}
return self;
The *value param has already been bound to my view model (successfully) and it gets updated each time a textfield changes.
What I'm looking for is a basic example or best-practice, the code above crashes when run so I know I'm missing something fundamental.
Thanks all
-filter: is simply passing values from RACObserve(self, value) through unchanged, but only if the block returns YES. So that means you're trying to set completed to values of whatever type value is. That's Probably Bad®.
But the good news is that you're really close!
Instead of filtering, you want to transform. You want to take every value and map it to something other thing. Namely whether that value passes validation. To do that, we use -map::
RAC(self, completed) = [RACObserve(self, value) map:^(NSString *fieldValue) {
return #([self validateCurrentValue]);
}];
I have a result coming from a method that is either of kind TWTweetComposeViewControllerResult or SLComposeViewControllerResult.
I need to pass this to a method. Something like
[self doSomething:result];
How do I declare this method?
- (void) doSomething:(SLComposeViewControllerResult) result ?
- (void) doSomething:(TWTweetComposeViewControllerResult) result ?
- (void) doSomething:(NSNumber *) result ?
Make two different methods that handle each. Each enum is a different type and should be treated as such. You can also forward the result on, using a boolean if you are just indicating success, or your own custom enum if you need more information.
- (void)doSomethingSL:(SLComposeViewControllerResult) result
{
// made up, idk what the result enum would be be
[self doSomething:(result == SLComposeSuccess)];
}
- (void)doSomethingTweet:(TWTweetComposeViewControllerResult) result
{
// made up, idk what the result enum would be be
[self doSomething:(result == TWTweetSuccess)];
}
- (void)doSomething:(BOOL)success
{
}
If you are still convinced that you want to handle them in a uniform way and ignore types, you could always cast the results to an int in the method and forward them on.
Both SLComposeViewControllerResult and TWTweetComposeViewControllerResult are both enums, with 0 meaning cancelled and 1 meaning done.
So any of these should be OK:
- (void) doSomething:(SLComposeViewControllerResult) result;
- (void) doSomething:(TWTweetComposeViewControllerResult) result;
- (void) doSomething:(NSInteger) result;
[edit] Note this comment in TWTweetComposeViewController.h:
// This class has been deprecated in iOS 6. Please use SLComposeViewController (in the Social framework) instead.
So you should just use the SLComposeViewControllerResult version.
I'm using a label to display the string result of function. However I have a class variable that stores the previous result and I need to update that variable in different ways depending on different conditions. The code I wrote is
if(displayPassword.text == #"Memorable")
{
prevpass = [newPassword returnPassword];
}
else
{
prevpass = displayPassword.text;
}
However it always jumps to the else as it seems to show under debugging that displayPassword.text is always empty depsite it showing a value.
You can only use == to compare scalar values. A string is an object. You need to use the isEqual: or isEqualToString: method instead.
if([displayPassword.text isEqualToString:#"Memorable"]) {