How to extract an argument of 'function' type from NSInvocation - objective-c

I am writing a unit test for the function that receives a protocol as input argument.
This function that I am testing calls some method of that protocol inside.
I want to mock this protocol and that method.
To mock the protocol using OCMock I wrote the following:
id<MyProtocol> myProtocol = OCMProtocolMock(#protocol(MyProtocol));
Now to mock the function I am using OCMStub.
The interesting part is that the function doesn't return any value but rather gets the callback as input argument and invokes it.
Here is its signature:
- (void)myFunction:(void (^ _Nonnull)(NSDictionary<NSString *, NSString *> * _Nonnull))completion;
I am writing the following code to mock this function:
OCMStub([myProtocol myFunction:[OCMArg any]])._andDo(^(NSInvocation *invocation){
void (^ _Nonnull)(NSDictionary<NSString *, NSString *> * _Nonnull) completion;
[invocation getArgument:&completion atIndex:0];
// Here I will invoke the completion callback with some dictionary and invoke the invocation
});
However I am getting the following error: "Expected identifer or '('". The error points to the line that defines completion variable.
How can I define the function variable of signature void (^ _Nonnull)(NSDictionary<NSString *, NSString *> * _Nonnull)?

That ain't a function. It is a block!
Regardless, both functions and blocks can be treated as void * in declarations. You'll need to cast them to the appropriate type after.
But that's probably the easiest way to deal with it; extract from the invocation as a void*, cast to a block, call it.

Actually I was able to extract a first argument by doing the following:
OCMStub([myProtocol myFunction:[OCMArg any]])._andDo(^(NSInvocation *invocation){
void (^ completion)(NSDictionary<NSString *, NSString *> * _Nonnull);
[invocation getArgument:&completion atIndex:2];
// Do other stuff
});
I was just declaring a variable of 'block' type incorrectly.
And I have also realized that the first argument should be accessed by index = 2;

Related

Is it possible that Bridging-header turns (void (^)(NSError *))block (ObjC) into block: () throws -> () (Swift)?

I have
OBJC:
- (void)doSomething:(void (^)(NSError *))block;
SWIFT:
let test = Test()
test.doSomething(<#T##block: ((Error?) -> Void)!##((Error?) -> Void)!##(Error?) -> Void#>)
I would rather
try? test.doSomething { }
I would like bridging-header to translate the function into
func doSomething(block: () throws -> ()) throws {
try block()
}
Is it possible? Thanks to all!
Your Objective-C method is declaring a parameter which is a block that receives an NSError object. It's basically declaring a callback.
If your method is not asynchronous you should declare it like this:
- (BOOL)doSomething:(NSError **)error;
Now the parameter is a pointer to an NSError* object. If the method fails for some reason, it should set an appropriate error for that parameter like so:
if (error != NULL) {
*error = <an appropriate error>
}
Also note the BOOL return type. According to Cocoa conventions the caller should refer to the return type to determine if the method failed or not, instead of testing for the existence of an NSError* object.
Declaring a method like this will expose it in Swift using the throws mechanics.
Update:
I don't think you can declare a Swift throwing block in Objective-C. If you go the other way around, and declare your desired method signature in Swift you'll see the compiler complains it can't be represented in Objective-C.
Most likely the (NSError **) to throwable convention never got implemented for blocks.

id pointer in Objective-C

I am new to Objective-C. I am coming from a Java background. I am trying to create a generic function in objective-c as follows:
- (id<BaseEntity>*)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>*) transferObject{
NSString * url = [[[self baseUurl] stringByAppendingString: [self getPath]]stringByAppendingString: #"/retreive/"];
url = [url stringByAppendingString:idValue];
NSMutableURLRequest *request = [self createGET:url];
NSString *responseString = [self processRequest:request];
BaseEntity* responseEntity = [transferObject unpack:responseString];
return responseEntity;
}
What I am trying to accomplish is create a function that can take a pointer to any object that implements the ITransferObject protocol. Use a function on this object as defined in the ITransferObject protocol. Then return a pointer to an object that implements the BaseEntity protocol. I feel like I took a Java generics approach to this and it is not working.
I get the following error:
Bad receiver type `__autoreleasing id<ITransferObject> *'
from this line:
BaseEntity* responseEntity = [transferObject unpack:responseString];
And I get the error:
Implicit conversion of an Objective-C pointer to `__autoreleasing id<BaseEntity> *' is disallowed with ARC
from this line:
return responseEntity;
Any ideas?
id is a pointer type in itself—you don’t need to append a * to it to represent a pointer as you do with a class.
- (id<BaseEntity>)retrieve:(NSString *)idValue unpackedWith:(id<ITransferObject>)transferObject
id is a special type - it is a by-reference type that does not require an asterisk: it is already a pointer. Here is an example that illustrates this point:
-(void)someMethod:(id)data; // id does not need an asterisk
...
NSString *str = #"Hello"; // NSString needs an asterisk
[self someMethod:str]; // This works
Adding an asterisk after an id makes the type a pointer to an id - i.e. a double pointer. There are legitimate situations when you need a pointer to an id, but it is not what you need in your situation.
You have two ways of fixing this issue:
You can change id for NSObject (similar to passing Object in Java), or
Remove the asterisk after the id.
The second approach is more common:
- (id<BaseEntity>)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>) transferObject;
First of all, it looks like BaseEntity is a class, not a protocol, so you can't use it as a protocol.
Second, the id type is inherently a pointer, so you normally don't want to declare a pointer-to-id.
Declare your method like this instead:
- (BaseEntity *)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>) transferObject{
(Note that I have changed the transferObject type by removing the *.)
Alternatively, use NSObject instead of id and leave the star, this:
- (BaseEntity*)retreive:(NSString *)idValue unpackedWith:(NSObject<ITransferObject>*) transferObject{
Also, the correct spelling is retrieve.

What (void(^)(NSString *)) as method parameter means in Objective-C?

I have to use some private API of my employer and i've got method like this:
- (void)login:(NSString *)username password:(NSString *)password delegate:(void(^)(NSString *))delegate;
What (void(^)(NSString *))delegate means?
This is parameter but i don't know what must be here.
This is a block pointer. If you are unfamiliar with blocks, this basically lets you assign an annonymous function inline as a parameter. The signature here says that the block takes an NSString as a parameter, and returns nothing. You would use it like this:
- (void)login:(NSString *)username password:(NSString *)password delegate:(void(^)(NSString *))delegate;
[someReceiver login:yourUsername password:yourPassword delegate:^(NSString *aString)
{
// This is the block (annonymous function). Do something with the aString paramter
}];
It denotes a block - it's essentially a closure (lambda function, whatever you call it) - Apple's addition to the C language. In this case, it returns void and accepts an NSString object as its only argument.

Why doesn’t my enum work as a method parameter?

I've used typedef enum in the past with method parameters and had no problem, but today I'm having problems...
h file
typedef enum
{ eGetVarious1,
eGetVarious2,
} eGetVarious;
- (double)getVarious:(eGetVarious)eVar:(NSDate*)pDate;
an m file
you're calling a class method, and declaring an instance method:
instance method:
- (double)getVarious:(eGetVarious)eVar:(NSDate*)pDate;
class method (may not use ivars or instance methods):
+ (double)getVarious:(eGetVarious)eVar:(NSDate*)pDate;
say you want this as an instance method, declare it like this:
- (double)getVarious:(eGetVarious)eVar forDate:(NSDate*)pDate;
and if you were in the scope of implementation of an instance method, then this should work:
double result = [self getVarious:eGetVarious1 forDate:[NSDate date]];
note the reason compiler is reporting an error:
if it has not seen a particular objc selector and you use it, it assumes the undeclared selector's arguments take id (anonymous objc object).
also, enum type should not be promoted to a pointer (although 0 is ok). since the compiler saw no way to match what you're calling: [objc_class* getVarious:eGetVarious<enum_type> :NSDate*] it is right, because you should be calling it as:
General * anInstanceOfGeneral = /* something here */;
NSDate * date = /* something here */;
double result = [anInstanceOfGeneral getVarious:eGetVarious1 forDate:date];

What does the "*" mean in Objective C?

Sometimes I encounter code that has *, sometimes **. Can anyone explain what they mean in Objective C? (I used to be a Java programmer, with experience in C/C++.)
The * denotes that you are using a pointer to a variable, and is most commonly used to store a reference to an Objective-C object, objects which can only live on the heap and not the stack.
Pointers are not a part of Objective-C exclusively, but rather a feature of C (and therefore its derived languages, of which Objective-C is one of them).
If you are questioning the difference between * and **, the first denotes a pointer, whereas the second denotes a pointer to a pointer; the advantage of the latter to the former is that when passing in an object using ** in a method parameter, the method can then change this parameter and the new value is accessible in the calling method.
Perhaps the most common use of ** in Cocoa is when using NSError objects. When a method is called that can return an NSError object on failure, the method signature would look something like this:
- (id)someMethodThatUsesObject:(id)object error:(NSError**)error;
What this means is that the calling function can pass in a pointer to an NSError object, but someMethodThatUsesObject: can change the value of error to another NSError object if it needs to, which can then be accessed by the calling method.
This is often used as a workaround for the fact that functions can only return one value.
A * in Objective-C means exactly the same as in C; and you'll usually see it (or not) in these situations:
// Method signatures:
// Here the asterisk (*) shows that you have a pointer to an NSString instance.
+ (NSString *)stringWithString:(NSString *)aString;
// Method signatures, part two:
// Here the double asterisk (**) signifies that you should pass in a pointer
// to an area of memory (NSError *) where outError can be written.
- (BOOL)writeToURL:(NSURL *) atomically:(BOOL) error:(NSError **)outError;
// Method signatures make for good examples :)
// Here the asterisk is hidden; id is a typedef for void *; and it signifies that
// a pointer to some object of an indeterminate class will be returned
- (id)init;
// And a forth example to round it all out: C strings!
// Here the asterisk signifies, you guessed it, a pointer! This time, it's a
// pointer to the first in a series of const char; terminated by a \0, also known
// as a C string. You probably won't need to work with this a lot.
- (const char *)UTF8String;
// For a bit of clarity, inside example two, the outError is used as follows:
// Here the asterisk is used to dereference outError so you can get at and write
// to the memory it points to. You'd pass it in with:
// NSError *anError;
// [aString writeToURL:myURL atomically:YES error:&anError];
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atom error:(NSError **)outError {
// do some writing, and if it went awry:
if (outError != NULL)
*outError = [NSError errorWithName:#"NSExampleErrorName"];
return NO;
}