Passing a Boolean pointer from Swift - objective-c

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.

Related

Swift parameters are not visible inside of Objective C Function?

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.

what is the proper syntax for the following expression in swift 3?

as you can guess, this is an issue regarding swift's whole API renovation. I've read the documentation extensively but can't seem to lay my finger on a proper workaround.
I am receiving the error
Value of type 'Error' has no member 'userInfo'
on the following line:
else if let secondMessage = error?.userInfo["error"] as? String
of this block:
let query = PFQuery(className: "Images")
query.whereKey("Subject", equalTo: self.subjectName)
query.findObjectsInBackground { (objects, error) -> Void in
if error == nil {
// do something
}else if let secondMessage = error?.userInfo["error"] as? String {
// do something
}
Any suggestions? feel's like I'm missing something painstakingly obvious... dev block i guess :(

Unable to set value via protocol

In my Objective C code I had this:
if ([view conformsToProtocol:#protocol(UITextInputTraits)]) {
id<UITextInputTraits> field = view;
field.enablesReturnKeyAutomatically = YES;
}
Now I'm trying to convert that to swift, so I did this:
if var field = view as? UITextInputTraits {
field.enabledReturnKeyAutomatically = true
}
I'm getting a compiler error saying that 'field' is immutable. What's the right way to accomplish this?
The problem is caused by Swift's peculiar way of dealing with optional protocol requirements. Optional protocol properties have no setter. (I regard this as a bug in the language.) You'll have to work around it.
You can say (horrible):
switch view {
case let field as UITextField:
field.enablesReturnKeyAutomatically = true
case let field as UITextView:
field.enablesReturnKeyAutomatically = true
default: break
}
Another way (equally horrible):
let setter = #selector(setter:UITextInputTraits.enablesReturnKeyAutomatically)
if view.responds(to:setter) {
view.perform(setter, with: 1 as NSNumber)
}

Macros with return value

I'm trying to reproduce this pattern in Swift
#define mustBeKindOfClassFailedReturn(object, objectClass, ret) \
if(![object isKindOfClass:objectClass]) { \
NSLog(([NSString stringWithFormat:#"%# must be kind of %# class, current class is %#", object, NSStringFromClass(objectClass), NSStringFromClass([object class])])) \
return ret; }
used like this
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
mustNotBeNilFailedReturn(self.adapter, UIEdgeInsetsZero)
mustBeKindOfClassFailedReturn(self.adapter, [WBCollectionViewSectionAdapter class], UIEdgeInsetsZero)
Does anyone know a good solution to this?
Edit
I know about guard but using guard i have to rewrite a lot of code each time i'll have to override a method in my subclasses for exemple
/**
* mustOverride
*/
#define mustOverride \
{ NSLog(#"You must override this function") }
#define mustOverrideFailedReturn(ret) \
{ mustOverride \
return ret; }
EDIT 2
I've ended with solution is it the optimal one ?
func needOverride(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) -> Bool {
REVLogManager.SharedInstance.logErrorMessage("You must override this function", function: function, file: file, line: line, exception: nil, error: nil)
return false
}
func doesObject(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__, matchingObject: AnyObject!, matchesClass matchingClass: AnyClass) -> Bool {
guard matchingObject.isKindOfClass(matchingClass) else {
let message = "\(matchingObject) must be kind of \(matchingClass) class, current class is \(matchingObject.dynamicType)"
REVLogManager.SharedInstance.logErrorMessage(message, function: function, file: file, line: line, exception: nil, error: nil)
return false
}
return true
}
I'm calling the method like this
public func actualScrollOffsetDistanceWithScrollView(scrollView: UIScrollView!) -> Float {
guard needOverride() else { return 0.0 }
return 0.0
}
And
guard doesObject(matchingObject: self, matchesClass: REVListSectionAdapter.classForCoder()) else { return }
Swift doesn't have a macro capability of this kind.
In terms of accomplishing your goal. The pattern you are following would most correctly be handled using guard statements. A function containing a guard statement must exit the enclosing scope (or trap) and may do so with a return.
Please check the Swift documentation for information about Control Flow and Early Exit control
My answer follows the same legacy pattern using in Swift. You can use classForCoder, but I prefer dynamicType for type checking.
func mustBeKindOfClassFailedReturn(object: AnyObject, objectClass: AnyClass, returnVal: Any?) {
if !object.isKindOfClass(objectClass) {
print("\(object) must be kind of \(objectClass) class, current class is \(object.dynamicType)")
return returnVal
}
return nil // or UIEdgeInsetsZero, or whatever
}
You may also get similar results with !(object is objectClass), but things get tricky when comparing certain number types. See Swift type inference and type checking issue
You can also replace AnyObject and Any with NSObject if you know you're dealing with Objective-C types. From your comment it seems like ret could be a value type, so that's why I make it an Any type.
Swift has a better solution for this kind of problem, the guard statement. In this case, just put this in your method.
guard let adapter = self.adapter as? WBCollectionViewSectionAdapter else {
/* return value if the adapter is not setup properly. */
return .Zero
}
/* continue with valid and correctly classed adapter */
If you're looking for a way to handle this for sub classes, you may want to consider generics. It will allow you implicit type safety without having to rewrite logic in your subclasses.

iap - conversion from objective c to swift

I'm trying to convert my app from objective c to swift, and i have everything except for the in app purchase working.
i have the objective c helper class imported in the project, but i'm having trouble doing the RequestProductsCompletionHandler section
in the old objective c version i have the code
[[MTIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
if([[self appData] isPro] == FALSE)
[[self bUpgrade] setUserInteractionEnabled:TRUE];
}
}];
and i'm trying to convert this section to objective c but so far i've been unable to convert the requestProductsWithCompletionHandler part
can anyone help?
i've tried creating the completion handler the same was as in objective c using
requestProductsWithCompletionHandler(sucess: Bool, products : NSArray)
but i get the compiler errors "Extra argument 'products' in call" and "Expected member name or constructor call after type name".
if i try it without the parameters i get "Missing argument for parameter #1 in call"
In Swift, your completion handler would be a closure. The syntax is semi-similar to blocks in Objective-C but, instead of defining the parameters outside the block:
^(BOOL success, NSArray *products) { /* ... */ }
you define them inside the closure:
{ (success: Bool, products: [AnyObject]!) in /* ... */ }
So, your call to requestProductsWithCompletionHandler should look something like this:
MTIAPHelper.sharedInstance().requestProductsWithCompletionHandler {
(success: Bool, products: [AnyObject]!) in
if success {
// etc.
}
}
You can also let Swift infer the parameter types for you:
MTIAPHelper.sharedInstance().requestProductsWithCompletionHandler {
(success, products) in
if success {
// etc.
}
}