changing a variable in completionblock - objective-c

Hello I am currently having issues bridging a variable in my completion block. I want to set isPreviewPlaying to NO in the completion block but I can't.
static void completionCallback (SystemSoundID mySSID, void *myself) {
AudioServicesRemoveSystemSoundCompletion (mySSID);
AudioServicesDisposeSystemSoundID(mySSID);
CFRelease(myself);
UIButton *previewButton = (__bridge UIButton*)myself;
[previewButton setTitle:#"Preview" forState:UIControlStateNormal];
_isPreviewPlaying = NO // I want to do this, but I can't.
}
- (void)previewButtonPressed:(id)sender {
if (_isPreviewPlaying) {
_isPreviewPlaying = NO;
NSLog(#"STOP");
AudioServicesDisposeSystemSoundID(soundID);
} else {
NSString * selectedPreviewSound = [self.soundFile objectAtIndex: _soundFileIndex];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath: selectedPreviewSound], &soundID);
AudioServicesPlaySystemSound (soundID);
_isPreviewPlaying = YES;
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, completionCallback, (__bridge_retained void *)sender);
[sender setTitle:#"Stop" forState:UIControlStateNormal];
}
}

You will need to pass the class instance to the completion block function, as it's a C function, and that way you can access properties and methods on the instance.
That is the intent of myself parameter I believe, however you are currently releasing it using CFRelease() for some reason I cannot fathom (I am assuming that sender is a button as previewButtonPressed: looks like a button event callback, and releasing it won't do it any favours; read crash).
Therefore I would suggest:
Remove the CFRelease() call.
Cast myself to whatever the class is: MyClass *myclass = (MyClass *)myself;.
Call myclass.previewPlaying = NO; (assuming it's a property). Call [myclass previewNotPlaying] (see below).
Pass self instead of sender to AudioServicesAddSystemSoundCompletion().
EDIT Having said that, I now see you are using the button instance to display information. Instead of calling the property, above, call a method on the instance instead and make that method do the work:
- (void)previewNotPlaying
{
// _previewButton is an IBOutlet to the button
[_previewButton setTitle:#"Preview" forState:UIControlStateNormal];
_isPreviewPlaying = NO;
}

Related

Are there any cons to a semi singleton?

I just solved a really weird problem I had where I needed this class to behave sort of like a singleton but not really. Here's a code snippet
#implementation HMFPicturePreviewModalPanel
__weak static UIViewController *presentingInventoryViewController = nil;
static HMFPicturePreviewModalPanel *sharedPicturePreviewModalPanel = nil;
+(void)showPopupWithImage:(UIImage *)image withStartPoint:(CGPoint)startPoint withStartView:(UIView *)startView {
//this checks if there is already a panel visible.
if (![presentingInventoryViewController.view viewWithTag:kHMFPicturePreviewModalPanelTag]) {
sharedPicturePreviewModalPanel = [[HMFPicturePreviewModalPanel alloc] initWithFrame:presentingInventoryViewController.view.bounds withimage:image];
[presentingInventoryViewController.view addSubview:sharedPicturePreviewModalPanel];
[sharedPicturePreviewModalPanel showFromPoint:[startView convertPoint:startPoint toView:presentingInventoryViewController.view]];
}
}
+(void)changePresentingInventoryViewController:(UIViewController *)newInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = newInventoryViewController;
}
+(void)removePresentingInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = nil;
}
Is this called a semi-singleton? There's only ever going to be one of these on the screen at a time. I had to recreate this each time for it to work, hence why I couldn't do it as just a singleton.
What are the cons to this solution?
Also is it okay to have a __weak static variable?

Use Block in Objective C to find out if a BOOL has been set?

I'm new to Obj-c. I've got a class which sets a var boolean to YES if it's successful (Game Center login = successful), what it would be great to do, is somehow have a listener to that var that listens to when it is YES and then executes some code. Do I use a block for that? I'm also using the Sparrow framework.
Here's my code in my GameCenter.m file
-(void) setup
{
gameCenterAuthenticationComplete = NO;
if (!isGameCenterAPIAvailable()) {
// Game Center is not available.
NSLog(#"Game Center is not available.");
} else {
NSLog(#"Game Center is available.");
__weak typeof(self) weakSelf = self; // removes retain cycle error
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; // localPlayer is the public GKLocalPlayer
__weak GKLocalPlayer *weakPlayer = localPlayer; // removes retain cycle error
weakPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[weakSelf showAuthenticationDialogWhenReasonable:viewController];
}
else if (weakPlayer.isAuthenticated)
{
[weakSelf authenticatedPlayer:weakPlayer];
}
else
{
[weakSelf disableGameCenter];
}
};
}
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)controller
{
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:YES completion:nil];
}
-(void)authenticatedPlayer:(GKLocalPlayer *)player
{
NSLog(#"%#,%#,%#",player.playerID,player.displayName, player.alias);
gameCenterAuthenticationComplete = YES;
}
-(void)disableGameCenter
{
}
But I need to know from a different object if that gameCenterAuthenticationComplete equals YES.
You can use a delegate pattern. It's far easier to use than KVO or local notifications and it's used a lot in Obj-C.
Notifications should be used only in specific situations (e.g. when you don't know who wants to listen or when there are more than 1 listeners).
A block would work here but the delegate does exactly the same.
You could use KVO (Key-Value Observing) to watch a property of your object, but I'd rather post a NSNotification in your case.
You'll need to have the objects interested in knowing when Game Center login happened register themselves to NSNotificationCenter, then post the NSNotification in your Game Center handler. Read the Notification Programming Topics for more details !
If there is a single method to execute on a single delegate object, you can simply call it in the setter. Let me give a name to this property:
#property(nonatomic,assign, getter=isLogged) BOOL logged;
It's enough that you implement the setter:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
[_delegate someMethod];
}
Another (suggested) way is to use NSNotificationCenter. With NSNotificationCenter you can notify multiple objects. All objects that want to execute a method when the property is changes to YES have to register:
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(handleEvent:) name: #"Logged" object: nil];
The handleEvent: selector will be executed every time that logged changes to YES. So post a notification whenever the property changes:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
{
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center postNotificationName: #"Logged" object: self];
}
}

Using a method in Objective-C cocoa

I'm having trouble with something very basic. I want to call this action:
- (IBAction)changeGreeting:(id)sender {
self.userName = self.textField.text;
NSString *nameString = self.userName;
if ([nameString length]==0) {
nameString = #"World";
}
NSString *greeting = [[NSString alloc] initWithFormat:#"Hello, %#!", nameString];
self.label.text = greeting;
}
When the user presses return after entering text in the text field. This is what I have:
-(BOOL)textFieldShouldReturn:(UITextField *)theTextField{
if (theTextField == self.textField) {
[theTextField resignFirstResponder];
[changeGreeting];
}
return YES;
}
I'm not sure what to put where it says "changeGreeting." Think I'm missing the concept here. Thanks for any help.
Your syntax is wrong. Try this:
[self changeGreeting:self];
In Objective-C the syntax for sending messages (calling functions) is like this:
[receiver message];
Since you implement the changeGreeting: method in the same class as you're calling it from, the receiver will be self. As the parameter (sender) you usually pass the object that sends the message, but since you don't use it in your implementation of changeGreeting: it doesn't really matter what you pass there.
you called the changeGreeting function in wrong way
you should call it like
[self changeGreeting:nil];
Or if you want to track something from the textField then
[self changeGreeting:theTextField];
the sender will get theTextField as parameter if you need some processing on based on the textField.

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

Methods are being called yet not executing

So my Twitter/Facebook implementation in my app has been a learning experience, but I'm almost there and I have one last, probably simple question. Using the MGTwitter engine, I'm calling a method from my viewcontroller in
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
The method is firing off, (confirmed by NSLog calls). However, it's not doing what it's supposed to do, which is fade in my logout button for Twitter. I'm still getting my hands around the way Objective-C handles methods and all, I feel like I'm just not pointing my variables to the right place. Any direction is much appreciated, here is the code below:
SA_OAuthTwitterEngine.m -
//
// access token callback
// when twitter sends us an access token this callback will fire
// we store it in our ivar as well as writing it to the keychain
//
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
if (!ticket.didSucceed || !data) return;
NSString *dataString = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
if (!dataString) return;
if (self.pin.length && [dataString rangeOfString: #"oauth_verifier"].location == NSNotFound) dataString = [dataString stringByAppendingFormat: #"&oauth_verifier=%#", self.pin];
NSString *username = [self extractUsernameFromHTTPBody:dataString];
if (username.length > 0) {
[self setUsername: username password: nil];
if ([_delegate respondsToSelector: #selector(storeCachedTwitterOAuthData:forUsername:)]) [(id) _delegate storeCachedTwitterOAuthData: dataString forUsername: username];
}
[_accessToken release];
_accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
//Call twit login from my view controller
MyView *fvController = [[MyView alloc] init];
[MyView twitLogin];
[MyView helper];
NSLog(#"LETS TWEET DIRECTLY AFTER SUCCESSFUL LOG IN!");
}
This is what my helper method is doing in my .m file:
-(void)helper{
NSLog(#"HELPER FUNCTION");
[self fadeIn:twitterLogout withDuration:2 andWait:2.0];
}
This is the method it's calling
//FADE IN FUNCTION ------------------------------//////////////////////
-(void)fadeIn:(UIView*)viewToFadeIn withDuration:(NSTimeInterval)duration
andWait:(NSTimeInterval)wait
{
[UIView beginAnimations: #"Fade In" context:nil];
[UIView setAnimationDelay:wait];
[UIView setAnimationDuration:duration];
viewToFadeIn.alpha = 1;
[UIView commitAnimations];
}
In Objective-C, methods are declared in one of two ways:
- (returnType)methodName;
or
+ (returnType) methodName;
The first type is an "instance" method and the second type is a "class" method.
These lines should be changed:
[MyView twitLogin];
[MyView helper];
Try this instead:
[fvController twitLogin];
[fvController helper];
Additionally, you may be calling your helper method before the delegate returns a value. You should see if the MGTwitterEngine contains a delegate. (I'd be surprised if it didn't.) You should use the available callbacks to call methods only when the login is finished. Simply calling the methods in order won't do what you want.
*Are both the log statements printed?
*Is setAccessToken:withData: being called from the main thread? Try calling the fadeIn method from the main thread i.e. something like this
- (void) helper{
NSLog(#"HELPER FUNCTION");
[self performSelectorOnMainThread:#selector(callFadeIn)];
}
-(void)callFadeIn{
[self fadeIn:twitterLogout withDuration:2 andWait:2.0];
}
See if that helps. It is possible that setAccessToken: (and thus helper and fadeIn) is being called from another thread. All UI operations should happen from the main thread.