Swift parameters are not visible inside of Objective C Function? - objective-c

Some background: I'm building an app in react native that uses an Objective C library written about 6 or 7 years ago, maybe older. I'm writing swift code that has been set up to send callbacks to the react native application in JS. I have this function that I'm trying to use:
token = service.getUserToken(server,
port: P2PFunctions.tls_port,
appId: P2PFunctions.appID,
appSecret: P2PFunctions.appSecret,
phone: P2PFunctions.phone,
token: nil, errcode: errCode, errmsg: nil);
callback(["\(token!)"]);
And this is its definition:
- (NSInteger)getUserToken:(NSString*)ip_In port:(NSInteger)port_In appId:
(NSString*)appId_In appSecret:(NSString*)appSecret_In phone:
(NSString*)phoneNum_In token:(NSString**)accessTok_Out errcode:
(NSString**)strErrCode_Out errmsg:(NSString**)errMsg_Out;
These are the types I'm using (EDIT: I changed them from private to public, and they still are not being recognized):
The problem is, I'm getting nil back from the function. I believe I'm getting an HTTP response that is empty, and I notice that inside the debugger when I step to the Objective C function, I see nil for all my parameters inside of the Objective C function. I think... it is that I'm not passing the correct type. Or my Swift parameters are not visible in Objective C's memory space. If it is expecting an (NSString *), should I be passing a String?
How do I pass the correct types from Swift to Objective C? What would I change in my function call? Are my parameter types okay? I cannot edit the original Objective C library. They share a common memory space for all variables in the entire program, right?
Thank you so much!

I just ran this successfully:
// the objc part
#interface Test : NSObject
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString* _Nonnull * _Nonnull)accessTok_Out
errcode:(NSString* _Nonnull * _Nonnull)strErrCode_Out
errmsg:(NSString* _Nonnull * _Nonnull)errMsg_Out;
#end
#implementation Test
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString**)accessTok_Out
errcode:(NSString**)strErrCode_Out
errmsg:(NSString**)errMsg_Out {
*accessTok_Out = #"Token";
*strErrCode_Out = #"OK";
*errMsg_Out = #"msg";
return 42;
}
#end
// and the swift part
let t = Test()
var token: NSString = "t"
var errcode: NSString = "c"
var errmsg: NSString = "m"
let result = t.getUserToken("ip", port: 1,
appId: "2", appSecret: "3", phone: "4",
token: &token, errcode: &errcode, errmsg: &errmsg)
print(result)
and it works as expected.
Maybe this gives you a hint as to what's different in your situation.

So, after spending a week or so on this problem, I realize that it was not that the parameters are not actually being passed, but that the debugger is just not displaying the values of those parameters, at least on the main thread. Because I was getting the error code, I thought that something had to be wrong with the way I called the function - but actually, the function call is fine, the variables just didn't appear in the debugger for some reason:
Variables above appear to be nil - but in fact, they do have values.

Related

RCT bridging with react native and objective C

I have been following this tutorial, which is talking about how to communicated from React Native and objective C. I followed the tutorial, and got a good result. The problem started when I wanted to add a new function called sendData. This would be called in JS and it would pass a string, and in objective C, it would receive the string.
But, the bridging wasn't working.
Here is my code:
RCT_REMAP_METHOD(sendData : (NSString*)str,
sendData_resolver:(RCTPromiseResolveBlock)resolve
sendData_rejecter:(RCTPromiseRejectBlock)reject)
{
BOOL response = [_cppApi sendData];
resolve(response);
}
This code gives the error No visible #interface for 'CCCppCom' declares the selector 'sendData' in Xcode. (CCCppCom is a header file)
another function I have, which is getData seems to work fine.
RCT_REMAP_METHOD(getData,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *response = [_cppApi getData];
resolve(response);
}
I'm not entirely sure what is going on here. I checked CCCppCom, and sendData is indeed there.
Can someone help me? Thanks in advance.
I solved the problem by using RCT_EXPORT_METHOD instead of RCT_REMAP_METHOD.

Passing a Boolean pointer from Swift

I am trying to call an objective-C method from swift. The method signature is:
-(BOOL)getPassThroughSync:(BOOL *)enabled error:(NSError **)error;
I am not yet able to pass in a Boolean pointer. Here is what I have so far:
var passThrough: Bool?
if scanner.getPassThroughSync(&passThrough, error: nil) {
}
This does not compile due to an invalid argument list.
Similarly, I want to call
-(BOOL)getUSBChargeCurrent:(int *)current error:(NSError **)error;
requiring an int pointer.
What am I missing?
Eventually, with help from the comments on other answers, the following worked:
var passThrough: ObjCBool = false
if scanner.getPassThroughSync(&passThrough, error: nil) {
cell.detailTextLabel?.text = passThrough ? "Yes" : "No"
}
Similarly,
var current: Int32 = 0
if scanner.getUSBChargeCurrent(&current, error: nil) {
cell.detailTextLabel?.text = String(current) + "mA"
}
I think the answer you're looking for is in the Pointers section of this Apple document. Essentially, you have to declare a pointer type: Type * maps to UnsafeMutablePointer<Type>.
To access the value stored in the pointer, use its memory property.
For more details on UnsafeMutablePointer see this document.

How to handle a NSDictionary as a return type in iOS swift?

I'm trying to convert and existing, working objective c application over to swift and I'm getting a little tripped up with "closures". Here's the old working objective c block that makes returns a value from a web service:
- (IBAction)didTapSayHiButton {
[self.meteor callMethodName:#"sayHelloTo" parameters:#[self.username.text] responseCallback:^(NSDictionary *response, NSError *error) {
NSString *message = response[#"result"];
[[[UIAlertView alloc] initWithTitle:#"Meteor Todos" message:message delegate:nil cancelButtonTitle:#"Great" otherButtonTitles:nil] show];
}];
}
So here I'm getting either getting back a dictionary or response. And it works.
And here's how I'm trying to go about this with swift (the method is slightly different):
#IBAction func sayHi(sender : AnyObject) {
var params = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: Dictionary<String, String>, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The error I'm getting in xCode: "NSDictionary is not a subtype of 'Dictionary'"
After looking through the swift book this is the best educated attempt that I can make. I've tried a few other things but each resulted in another type of error.
How do I make this work with swift?
Edit: I've also tried just using Dictionary and Dictionary<String, String>
I should also note that I'm using a bridging header to access objective c code (objectiveDDP). And that callMethodNamed is written in objective c as can be seen here: https://github.com/boundsj/ObjectiveDDP/blob/master/ObjectiveDDP/MeteorClient.h#L47
Update: by changing the method to:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback:nil)
we were able to get it to work. But the second we try to add in the closure, it starts throwing the same original errors.
Try changing from using a Swift dictionary to explicitly using NSDictionary:
#IBAction func sayHi(sender : AnyObject) {
var params: NSDictionary = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: NSDictionary!, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The trick, in this particular case, is to completely omit the closure's parameter types, and let the compiler figure it out. After searching around for a while, I found this post which led me to the solution.
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
response, error in
let me:String = response["result"] as String!
println("called back \(me)")
})
If your not worried about accessing the closures parameters, apparently you can also using an underscore to ignore them completely:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
_ in
// Do something here
})

PRTweenOperation timingFunction member not found in Swift

I'm trying to use the PRTween library in a Swift iPhone app.
Original example code from GitHub:
PRTweenPeriod *period = [PRTweenPeriod periodWithStartValue:100 endValue:200 duration:3];
PRTweenOperation *operation = [[PRTweenOperation new] autorelease];
operation.period = period;
operation.target = self;
operation.timingFunction = &PRTweenTimingFunctionLinear;
My Swift port:
var period = PRTweenPeriod.periodWithStartValue(100, endValue: 200, duration: 3) as PRTweenPeriod
var operation = PRTweenOperation()
operation.period = period
operation.target = self
operation.timingFunction = PRTweenTimingFunctionLinear
Xcode is giving me this error:
'PRTweenOperation' does not have a member named 'timingFunction'
I'm not sure how to fix this. I can clearly see the member definition in PRTween.h. I'm thinking it might be related to the fact that this is where the definition of PRTweenTimingFunction takes me.
typedef CGFloat(*PRTweenTimingFunction)(CGFloat, CGFloat, CGFloat, CGFloat);
Has anyone else seen an error like this? Any suggestions for fixes?
P.S. I'm not really sure what to call that typedef. Is it a function pointer?
EDIT
As a workaround, I used this code that does not ask for a timing function:
let period = PRTweenPeriod.periodWithStartValue(100, endValue: 200, duration: 2) as PRTweenPeriod
PRTween.sharedInstance().addTweenPeriod(period,
updateBlock: { (p: PRTweenPeriod!) in
NSLog("\(Int(p.tweenedValue))"
},
completionBlock: { NSLog("Completed tween") })
Yes, that's a function pointer. This is a current limitation of C interoperability:
Note that C function pointers are not imported in Swift.
You might consider filing a bug if you'd like this to work. (Note that block-based APIs are fine and work with Swift closures.)

How to use "enumerateChildNodesWithName" with Swift in SpriteKit?

I'm using Swift to make a game in SpriteKit.
In Objective-C I could use the following method:
(void)enumerateChildNodesWithName:(NSString *)name usingBlock:(void (^)(SKNode *node, BOOL *stop))block
to perform actions on that *node, but I can't get this function working in Swift. Basically, I don't know how to reference that node in Swift.
This is the code I'm using, but I'm having trouble with the "usingBlock:" part. I've tried many things for many hours, but have not succeeded. Help please!
func spawnEnemy() -> () {
let enemy = SKSpriteNode(imageNamed: "enemy")
enemy.name = "enemy"
enemy.position = CGPointMake(100, 100)
self.addChild(enemy)
}
func checkCollisions() -> () {
self.enumerateChildNodesWithName("enemy", usingBlock: ((SKNode!, CMutablePointer<ObjCBool>) -> Void)?)
}
For now, don't trust autocomplete to insert the code you need — it drops in signatures from the "header", but a block signature is not the same as the declaration you need when inserting your own closure for a block parameter.
The formal way to write a closure would be to replicate the signature inside braces, adding local parameter names and using the in keyword to mark the start of the closure body:
self.enumerateChildNodesWithName("enemy", usingBlock: {
(node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
// do something with node or stop
})
But Swift's type inference means you don't have to write that much. Instead, you can just name the parameters, because their type (as well as the closure's return type) is known:
self.enumerateChildNodesWithName("enemy", usingBlock: {
node, stop in
// do something with node or stop
})
You can also use trailing closure syntax:
self.enumerateChildNodesWithName("enemy") {
node, stop in
// do something with node or stop
}
(You can even drop the local parameter names and refer to parameters by position — e.g. $0 for node — but here isn't a great place to do that because it makes your code far less readable. It's best to reserve $0 and friends for closures where it's blindingly obvious what the parameters are, like the closures you use with map and sort.)
See Closures in The Swift Programming Language for further explanation.
Also, because stop is an UnsafeMutablePointer, the syntax for using it is a bit different than in ObjC: set stop.memory = true to break out of enumeration.