Swift call Objective-C wrapper function containing blocks - objective-c

I have an Objective-C wrapper (ObjCWrapper.h and ObjCWrapper.m) with prototype
+ (void) login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock;
With typedef
typedef void (^SuccessBlock)(NSString *);
and implementation
+ (void)login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock
{
// do stuff like
successBlock(#"test");
}
From my swift view controller (ViewController.swift), I call the login function:
ObjCWrapper.login("abc", andPassword: "abc",
andErrorBlock:
{
(error:String) -> Void in
println();
},
andSuccessBlock:
{
(map:String) -> Void in
println();
}
)
But I get error:
Cannot invoke 'login' with an argument list of type '(String, andPassword: String, andErrorBlock:(String)->void, andSuccessBlock:(String)->void)'
Searching in google says that I am passing some invalid types in the arguments, but I can't find anything wrong in the code. Removing the blocks from the function makes the code work, so I guess it is something related on the way of calling a block function.
Thanks for the help!

It might be worth adding nullability specifiers to your completion block:
typedef void (^SuccessBlock)( NSString * _Nonnull );
And to the method itself:
+ (void) login:(nonnull NSString *)username andPassword:(nonnull NSString *)password andErrorBlock:(nullable SuccessBlock)errorBlock andSuccessBlock:(nullable SuccessBlock)successBlock;
Then you should be able to call your method in Swift:
ObjCWrapper.login("login", andPassword: "pass", andErrorBlock: { (error:String) -> Void in
//error handling
}) { (map:String) -> Void in
//other stuff
}

Obj-C NSString != Swift String
With other word, you pass a String where a NSString is expected.
Casting it down should solve that.

This is what I ended up doing
let username = "username" //usernameField.text
let password = "password" //passwordField.text
ObjCWrapper.login(username, andPassword: password,
andErrorBlock:
{
(map) -> Void in
// stuff
})
{
(map) -> Void in
// stuff
}

Related

Objective-C: What does this code mean?

In the MyViewController.h file:
#property (nonatomic, copy, nullable, class) void (^saveMetadataSuccess)(MyViewController*const _Nullable myViewController);
In the MyViewController.m file:
void (^saveMetadataSuccess)(MyViewControllerr* const myViewController) = nil;
+ (void)setSaveMetadataSuccess:(void (^)(MyViewController* const))newMetadataSaveSuccess {
saveMetadataSuccess = [newMetadataSaveSuccess copy];
}
+ (void (^)(MyViewController* const))saveMetadataSuccess {
return saveMetadataSuccess;
}
And finally the method which I don't understand:
- (void)success {
dispatch_async(dispatch_get_main_queue(), ^{
MyViewController.saveMetadataSuccess(self);
});
}
From my understanding, saveMetadataSuccess is a getter, but MyViewController.saveMetadataSuccess(self);seems to set something.
Can somebody enlighten me?
Thanks
MyViewController.saveMetadataSuccess is a getter and it returns a block that then being called with a param (self).
So it's like a function that returns other function.
Also you must not just call MyViewController.saveMetadataSuccess(self); because MyViewController.saveMetadataSuccess is nullable and it will crash if MyViewController.saveMetadataSuccess is null.
You have to check MyViewController.saveMetadataSuccess first:
- (void)success {
dispatch_async(dispatch_get_main_queue(), ^{
if (MyViewController.saveMetadataSuccess) {
MyViewController.saveMetadataSuccess(self);
}
});
}

OCMock mock protocol's static class method.

New in OCMock 3 is the ability to mock out class methods.
Is it possible to mock class methods defined in a protocol? i.e
#protocol AViewControllerProtocol <NSObject>
+ (Type)typeForViewController;
#end
Inside my unit test class
- (void)testProtocolClassMethod {
id mockedViewController = OCMProtocolMock(#protocol(AViewControllerProtocol));
//This line compiles fine, but throws an exception at run time.
OCMStub([mockedViewController typeForViewController]).andReturn(SomeType);
}
Exception throw
NSInvalidArgumentException: cannot stub/expect/verify method 'typeForViewController' because no such method exists in the mocked class
This looks like it was an oversight in OCMock 3.1, but you can make the fix yourself, if you want.
// OCProtocolMockObject.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, YES);
if(methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, YES);
}
// Add this case for required class methods
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, NO);
}
// Add this case for optional class methods
if (methodDescription.name == NULL)
{
methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, NO);
}
if(methodDescription.name == NULL)
{
return nil;
}
return [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
}
I verified this fix with this test:
- (void)testProtocolClassMethod {
id mockedViewController = OCMProtocolMock(#protocol(AViewControllerProtocol));
// FIXED: This line compiles fine, but throws an exception at run time.
OCMStub([mockedViewController typeForViewController]).andReturn(SomeType);
Type type = [mockedViewController typeForViewController];
XCTAssertEqual(type, SomeType, #"Not equal!");
OCMVerify([mockedViewController typeForViewController]);
}
I'll put a request in on the project page for this.

How can method in Swift with inout parameter be used in Objective-C?

I want
func foo(inout stop: Bool) -> Void {
// ...
}
use in my Objective-C part. But it is never generated in Module-Swift.h header. If I mark it with #objc, the
Method cannot be marked #objc because the type of the parameter cannot
be represented in Objective-C
error occurs.
You can't use an inout parameter when bridging with Objective-C, but you can do something similar if you use an UnsafeMutablePointer<T> (T would be Bool in your case). It would look something like this:
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
if stop != nil {
// Use the .pointee property to get or set the actual value stop points to
stop.pointee = true
}
}
Example
TestClass.swift:
public class TestClass: NSObject {
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
stop.pointee = true
}
}
Objective-C:
TestClass *test = [[TestClass alloc] init];
BOOL stop = false;
[test foo:&stop];
// stop is YES here
Similarly to what happening with generics, inout is not objc-compatible.
One possible workaround is to embed your parameter(s) in a class (which is a reference type, hence passed by pointer and not by value):
#objc class MyFuncParams {
var stop: Bool
init(stop: Bool) {
self.stop = stop
}
}
and define the function to accept an instance of that class:
func changeParam(params: MyFuncParams) {
params.stop = true
}
Not an elegant way to solve the problem, but what's important is that it should work (never tried myself though).

Objective-C passing parameters in void method

How would I go about passing parameters when calling a void method? I understand that you can do something like this:
-(void)viewDidLoad {
[self callMethod];
}
-(void)callMethod {
//stuff here
}
But how would I pass a parameter, such as an NSString, to the callMethod method?
Here is an example with an integer parameter.
-(void)viewDidLoad {
[self callMethodWithCount:10];
}
-(void)callMethodWithCount:(NSInteger)count {
//stuff here
}
In objective-c the parameters are included within the method name. You can add multiple parameters like this:
-(void)viewDidLoad {
[self callMethodWithCount:10 animated:YES];
}
-(void)callMethodWithCount:(NSInteger)count animated:(BOOL)animate{
//stuff here
}
It seems you may be misunderstanding what the void in the beginning of the method means. It's the return value. For a void method, nothing is returned from calling the method. If you wanted to return a value from your method you would do it like this:
-(void)viewDidLoad {
int myInt = [self callMethodWithCount:10 animated:YES];
}
-(int)callMethodWithCount:(NSInteger)count animated:(BOOL)animate{
return 10;
}
You define your method to return an int (in this example it always returns 10.) Then you can set an integer to the value returned by calling the method.
- (void)callMethod:(NSString *)string
{
}
Where string is your parameter so you would call
NSString *myString = #"your string here......";
[self callMethod:myString];

Send Objective-C message to C void *

Two questions:
Is it possible to send objective-c message to C void * from C function?
Is it possible to hint void * with <SomeProtocol> in C-function declaration? In function body?
(pseudocode)
// myfunc.h
void myfunc(void *object, int param);
// myfunc.c
void myfunc(void *object, int param) {
// desired (pseudocode):
// [<SomeProtocol>(id)object method:param];
}
// objective-c controller
# include "myfunc.h"
// ....
#implementation
- (void)visible_to_outer_world {
Object *o = [Object new];
myfunc(o, 5);
}
// ....
#end
Is it possible to send an Objective-C message to a void * from a C function?
Not sure why you would want it, but if you're compiling as Objective-C:
void bar(void *ptr)
{
// MRC version:
[(id)ptr someMessage];
// ARC (alias "ugly") version:
[(__bridge id)ptr someMessage];
}
Foo *foo = [[Foo alloc] init];
bar(foo);
Is it possible to hint void * with <SomeProtocol> in a C function declaration? In a function body?
No.