Objective C long bridged to Swift as int? - objective-c

I can't bridge ObjectiveC long type into Swift.
//Swift
typealias Long = Int64
struct Item: Equatable {
let itemId: Long
...
}
//ObjC
#interface MyObject : NSObject
#property(nonatomic, assign) long itemId;
//Try create object in Swift
Item(itemId: objCObject.itemId)
Error:
Cannot convert value of type 'Int' to expected argument type 'Long' (aka 'Int64')

The (Objective-)C type long is bridged to Swift as CLong which is an alias for the Swift Int type. Int can be a 32-bit or 64-bit integer, depending on the platform.
In any case, you cannot cast or assign an Int64 directly to an Int, see also Can I cast Int64 directly into Int?.
If you want to keep the Swift itemID as a Int64 then you'll have to convert it to CLong when calling the Objective-C method. Alternatively you can change the Swift type to make it compatible:
struct Item: Equatable {
let itemId: CLong
...
}
Another option would be to change the Objective-C type to
#property(nonatomic, assign) int64_t itemId;
which is then imported to Swift as Int64.

Related

Avoiding "Interface type cannot be statically allocated" after converting Objective-C typedef to Swift enum

I have a typedef definition in Objective-C like this:
typedef NS_ENUM(NSInteger, MyStatus) {
MyStatusUnknown = -1,
MyStatusBad = 0,
MyStatusGood = 1
}
This enum is in a pod, and it's used by several downstream pods, and the app like this:
#property (readonly, nonatomic, assign) MyStatus myStatus;
I need to convert class with typedef to Swift without changing the signature of enum definition in downstream pods.
I tried a straight forward conversion first:
enum MyStatus: Int {
case unknown = -1
case bad
case good
}
But in this case, the property statement above will show an error:
Interface type cannot be statically allocated
This error can be resolved by changing property to
#property (readonly, nonatomic, assign) MyStatus *myStatus;
but as I said before, I cannot change this property definition.
So how to convert typedef to Swift, while maintaining its backward compatibility with existing Objective-C property?
In your *.swift file you have to declare (#objc is required)
#objc
public enum MyStatus: Int {
case unknown = -1
case bad
case good
}
Then autogenerated -Swift.h will contain
typedef SWIFT_ENUM(NSInteger, MyStatus, closed) {
MyStatusUnknown = -1,
MyStatusBad = 0,
MyStatusGood = 1,
};
including that -Swift.h in precompiled headers (or directly if needed) will give that declaration available for Objective-C file having myStatus property.

BOOL is incompatible with Bool in Swift 4.1

When overriding in a Swift subclass of an Objective-C class, I get a message saying:
Property type 'BOOL' (aka 'bool') is incompatible with type 'Boolean' (aka 'unsigned char') inherited from 'ChildClass'
I tried to use Other Boolean types but it won't work.
Any idea how to properly override an Objc BOOL in Swift
Swift code (subclass):
override var myVar: Bool {
get {
return something ? true : myVar
}
set {
myVar = newValue
}
}
Objc Parent Declaration:
#property(atomic) Boolean isLoading;
Swift bridging header where the warning appear:
SWIFT_CLASS("_TtC6Module30ChildClass")
#interface ChildClass : ParentClass
#property (nonatomic) BOOL myVar; //<----- Here
#end
in ObjC BOOL and bool are not the same (BOOL is a signed char, whereas bool - aka C bool- is an unsigned char). Boolean is typedef to unsigned char as well, so is a C bool.
Can you change your objC property to BOOL?
if not, then use the swift type 'CBool'.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html

Can't access from Objective-c to swift's member properties

I have swift class like this.
class Note: NSObject{
var soundUrl: URL!
var sPosi: Int!
var contNum: Int!
}
then from Objective-c I would like to access these members.
However There is difference betwwen URL type and Int type.
I think I include correctly in both (.h) (.m) file
in Quesion.h
#class Note
#interface Question : NSObject{
Note * QNotes[70];
}
in Question.m
#import "QuesionNote-Swift.h"
- (id)init
{
// QNote array correctly declared and have each Note class instance.
int i = QNotes[i].sPosi; // Question.m:135:26: Property 'sPosi' not found on object of type 'Note *'
int j = QNotes[Q].contNum; //Question.m:145:37: Property 'contNum' not found on object of type 'Note *'
NSURL * k = QNotes[QPosiNum].soundUrl // it doesn't show compile error!!

Block with multiple parameters as argument in swift/objective-c

I'm trying to pass a Swift function (with multiple parameters, defined as a closure) to an Objective-C function. Technically, the function is defined in Objective-C++, but I'm encountering an issue at the header level, so it should be equivalent for this question.
In the Objective-C header, I have this defined as:
#interface MyObjCClass : NSObject
typedef void (^MyCallback)(NSMutableData*, int);
- (void) functionThatTakesACallback: (MyCallback) callback;
In swift, I'm trying to use it in this way:
self.objcclass!.functionThatTakesACallback()
{
(values: NSMutableData, length: Int32) -> Void in
// Do something with this data.
}
The error I get is:
Cannot convert value of type '(NSMutableData, Int32) -> Void' to expected argument type 'MyCallback!'
If I unwrap that a bit, by putting the block directly into the Objective-C function definition (sans typedef):
- (void) functionThatTakesACallback: (void (^)(NSMutableData*, int)) callback;
it gives me the vaguely more helpful:
Cannot convert value of type '(NSMutableData, Int32) -> Void' to expected argument type '((NSMutableData!, Int32) -> Void)!'
So, essentially, is there any way to unwrap this closure to fit the expected type? Or redefine the Objective-C type?
I think you'll need to add nullability attributes to your callback parameter and to the callback argument for your function. There are two ways you could do it.
Add the nullability annotations to each place that needs it.
#interface MyObjCClass : NSObject
typedef void (^MyCallback)(nonnull NSMutableData*, int);
- (void) functionThatTakesACallback: (nonnull MyCallback) callback;
Set the whole file to assume nonnull
NS_ASSUME_NONNULL_BEGIN
#interface MyObjCClass : NSObject
typedef void (^MyCallback)(NSMutableData*, int);
- (void) functionThatTakesACallback: (MyCallback) callback;
#end
NS_ASSUME_NONNULL_END
I actually just managed to fix this, but because the solution is simple but rather non-intuitive given the error message, here is the solution:
The function call from Swift must actually be:
self.objcclass!.functionThatTakesACallback()
{
(values: NSMutableData!, length: Int32!) -> Void in
// Do something with this data.
}
For the parameter values Objective-C is expecting a pointer to an object and that pointer could be nil. In Swift that translates to an optional value. An optional is followed by a ? and should be unwrapped - checked for nil and if there's an object there then return it.
Try:
self.objcclass!.functionThatTakesACallback() {
(values: NSMutableData?, length: Int32) -> Void in
if let values = values { // first values is a local, second values is unwrapped into it
// Do something with this data.
}
}

Implement Objective c protocol in Swift

I have this protocol in a objective c class:
#protocol YTManagerDelegate <NSObject>
#required
- (void)uploadProgressPercentage:(int)percentage;
#end
...
and a swift class connected to it:
class YTShare: UIViewController, YTManagerDelegate{
func uploadProgressPercentage(percentage:Int?){
println(percentage)
}
...
I receive the error: type YTShare does not conform to protocol YTShareDelegate, I have probably write incorrectly the swift function so the obj class don't find it. How I can write it correctly?
There are two errors in the delegate method
func uploadProgressPercentage(percentage:Int?){
println(percentage)
}
The parameter must not be an optional, and the C type int is mapped to Swift
as CInt (an alias for Int32):
func uploadProgressPercentage(percentage:CInt){
println(percentage)
}
Alternatively, you could use NSInteger in the Objective-C protocol, which is
mapped to Int in Swift. This would be a 32-bit or 64-bit integer, depending
on the architecture, whereas int/CInt is always 32-bit.