I have a method with a callback that looks something like this:
- (void)doStuff:(void ^())callback
{
//Do a whole bunch of stuff
//Perform callback
callback();
}
I would then call this method later on like this:
[self doStuff:^{[self callbackMethod];}];
This works just fine when there is no data to pass, but now I have some data that I need to pass between the methods.
Take the following method:
- (void)showAViewWithOptions:(int)options
In this method, I show a view with certain options, but if there's something else already on the screen, I call the method to hide it with a callback back to this method.
So the implementation looks like this.
- (void)hideOldView:(void ^())callback
{
//Hide all objects in _oldViews and set _oldViews = nil
callback();
}
- (void)showAViewWithOptions:(int)options
{
if(_oldViews != nil)
{
[self hideOldView:^(int options){[self showAViewWithOptions:options];}];
return;
}
//Show the new view
}
This compiles and runs without issue, but options loses its value after being passed.
Quite frankly, it surprised me that it compiled, since I thought it wouldn't accept a block with arguments.
For instance, if I call [self showAViewWithOptions:4];, when the callback is fired, options = -1730451212.
How do I bind the value options to the block? Or a better question, is this simply not possible because when I call the callback:
callback();
I'm not putting anything into the parentheses?
If so, then a good follow-up question would be: why does this even compile in the first place?
This should work:
- (void)showAViewWithOptions:(int)options
{
if(_oldViews != nil)
{
[self hideOldView:^(){
// Recursion doesn't feel right; be careful!
// Why can't whatever is being done by this call be done
// within this block?
[self showAViewWithOptions:options];
}];
return;
}
//Show the new view
}
A block with a return value and parameters looks like this:
^ return_type (parameter1_type parameter1_name, parameter2_type parameter2_name, ...) {
do_stuff;
};
you can pass vairable into method... Callback method you call inside method:
- (void)hideOldViewWithId:(float)f callback:(void (^)(float f))callback{
f = f + 2.0f;
callback(f);
}
and then call
[self hideOldViewWithId:1.0f callback:^(float f) {
NSLog(#"callback with float: %f", f);
}];
Related
I am trying to call a completion block in a unit test but it never reaches.
Here is the code:
[vc configureRecorder:^{
NSLog(#"Completion...");
}];
This is the method:
-(void)configureRecorder:(void(^)(void))callback {
NSLog(#"Method");
}
You need to call the completion block at the end of your method. like this:
-(void)configureRecorder:(void(^)(void))callback {
//#"Method"
callback()
}
Is it possible to construct a closure in objective-c and pass it to javascript where it can be invoked? The specific problem I am trying to solve is adding support for changing shipping methods and contacts in Apple Pay as part of the tipsi-stripe react-native module (something it doesn't do yet). This is basically what I have so far, but the callback in javascript gets 'null'.
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingMethod: (PKShippingMethod *) shippingMethod
completion:(nonnull void (^)(PKPaymentAuthorizationStatus, NSArray<PKPaymentSummaryItem *> * _Nonnull))completion {
id callback = (void (^)(NSArray* summaryItems)) {
completion(PKPaymentAuthorizationStatusSuccess, nil, summaryItems);
}
[self sendEventWithName: "#ShippingMethodChanged" body:#{#"selectedMethod": #"someMethodDetails", #"callback": callback}];
}
In javascript, I have something like this:
import { NativeEventEmitter, NativeModules } from 'react-native'
const { TPSStripeManager } = NativeModules;
const stripeEventEmitter = new NativeEventEmitter(TPSStripeManager);
componentWillMount() {
this.stripeOnShippingMethodChanged = stripeEventEmitter.addListener(
'ShippingMethodChanged',
(method, callback) => {
// async compute some value then
let summaryItems = await computeItemsWithMethod(method);
callback(summaryItems);
}
);
}
componentWillUnmount() {
this.stripeOnShippingMethodChanged.remove();
}
I'm assuming I have to somehow wrap the Objective-C closure so javascript knows how to invoke it but I can't find anything. Any help appreciated!
It is not possible to do what I was attempting to do here, the react-native bindings only support simple types that can be encoded as a string. The solution that I ended up with based on patterns I found elsewhere is to store the completion as a property on the object, trigger an event that can be seen in js-land and provide another exported method that can be called to trigger the completion. Code is here:
https://github.com/tipsi/tipsi-stripe/pull/244/files
How can you optionally pass in a function to another function? Concretely, assume functionA accepts as a parameter functionB. If functionB is not nil, how can you execute functionB inside of functionA?
Hard to see what's hard about this, so maybe this isn't what you mean, as it seems so trivially obvious; but anyway, here goes:
func optionalFunctionExpecter(f:(()->())?) {
f?()
}
Here's how to call it with an actual function:
func g() {
println("okay")
}
optionalFunctionExpecter(g)
Or, calling it with an actual anonymous function:
optionalFunctionExpecter { println("okay2") }
Here's how to call it with nil:
optionalFunctionExpecter(nil)
Observe that in my implementation of optionalFunctionExpecter, when called with nil, nothing will happen: we won't call f, we won't crash, we won't do anything. If you need to know that nil was passed, you can readily find out: just ask, in optionalFunctionExpecter, whether f == nil and proceed on that basis as desired. For more flexibility, we could rewrite like this:
func optionalFunctionExpecter(f:(()->())?) {
if let f = f {
f()
} else {
// ... nil was passed, respond to that fact ...
}
}
I have 4 void statements and I want to randomize them so one out of the 4 triggers at a time. Like for example the first void triggers then the next time maybe the third void triggers and so fourth. Could I use arc4random() or do I need another approach?
Sure you can use arc4Random. (It's better to use arc4random_uniform, as #JustSid pointed out in his comments. Off to fix my sample code...) There are a nearly infinite number of ways to do this.
First, a gripe of mine. Don't call methods "voids". That's inaccurate and misleading. (And it makes you sound ignorant about programming.) They're methods. The text inside the parenthesis at the beginning of the method tells you what kind of value it returns. If it doesn't return anything, the word "void" is C language notation for "nothing."
So the method:
-(void) foo;
Takes no parameters and doesn't return anything, where the method:
-(BOOL) bar;
...also takes no parameters, but it returns a boolean result.
The first method is not a "void". It is a method that doesn't return a result.
Now, to your question:
You could do something like this:
- (void) foo;
{
NSLog(#"foo");
}
- (void) bar;
{
NSLog(#"bar");
}
- (void) foobar;
{
NSLog(#"foobar");
}
- (void) randomMethod;
{
int index = arc4random_uniform(3);
switch (index)
{
case 0:
[self foo];
break;
case 1:
[self bar];
break;
case 2:
[self foobar];
break;
}
}
You could also use blocks. You could set up an array of block pointers, use arc4random_uniform() to pick an array index, and execute the appropriate block from the array. (Blocks are objects so you can add them to an array.)
The syntax of blocks and block pointers is a little tricky to follow, so for simplicity I'm not going to write that out. If you're interested I can amend my answer to show how that's done.
arc4random() is perfect for that.
int a = arc4random() % 4;
switch (a) {
case 0:
[self void0];
break;
case 1:
[self void1];
break;
// ...
}
For a scalable solution, you can use NSSelectorFromString.
The NSSelectorFromString will generate this warning: "performSelector may cause a leak because its selector is unknown". If you can't live with the warning, there are solution for that.
NSArray *methods = #[#"method1", #"method2", #"method3", #"method4"]; //add more if needed
int index = arc4random_uniform((int)methods.count);
NSString *selectedMethod = [methods objectAtIndex:index];
SEL s = NSSelectorFromString(selectedMethod);
[self performSelector:s];
-(void)method1
{
NSLog(#"method1 is called");
}
-(void)method2
{
NSLog(#"method2 is called");
}
-(void)method3
{
NSLog(#"method3 is called");
}
-(void)method4
{
NSLog(#"method4 is called");
}
I am going around blocks and try to discover the ways that they can be used.
So I am wondering is it possible to pass block to block like parameter?
Here is some sample code:
//declaration
static id (^someBlock)(id) = ^(id someClass) {
// do some stuff to obtain class some class instance
// check if class instance respond to #selector
// if yes - perform selector
}
//usage
+ (instancetype)someMethod {
someBlock(SomeClass.class);
// do additional work and return some instance type
}
This works fine, but is not good enough, because we obligate caller to respond to selector if caller want to do some additional stuff when someBlock is completed.
So my question is how I can invoke someBlock block with parameter block which I want to be executed when someBlock is completed.
Some like:
//declaration
static id (^someBlock)(id, <b>^otherBlock</b>) = ^(id someClass, <b>????</b>) {
// do some stuff to obtain class some class instance
otherBlock();
}
Any advice?
PS: Please note that the question is not about passing block to method as parameter.
Thanks,
Venelin
Is this what you are looking for?
static id (^someBlock)(id, void (^otherBlock)()) = ^id (id someClass, void (^otherBlock)()) {
otherBlock();
return nil; // just because you declares a `id` return type
};
And call it like
someBlock(someClass, ^() {
NSLog(#"other stuff");
});