How to type-hint and return an array of integers from Objective-C into Swift?
In Swift:
var x: [Int]
x = some.data()
In Objective-C, if data()
is defined as:
- (NSArray<NSInteger> *)data;
Xcode give an error type argument 'NSInteger' (aka 'long') is neither an Objective-C object nor a block type
If data()
is defined as:
- (NSArray<NSNumber *> *)data;
Xcode give an error Cannot assign value of type '[NSNumber]' to type '[Int]'
If data()
is defined as:
- (NSArray *)data;
Xcode give an error Cannot assign value of type '[Any]' to type '[Int]'
Is there a way to type-hint an array of integer in Objective-C?
CodePudding user response:
NSArray
must contain objects like NSNumber
, scalar types like NSinteger
are not supported.
So you have to define
- (NSArray<NSNumber *> *)data;
On the Swift side just force bridge the array to [Int]
var x: [Int]
x = some.data() as! [Int]
CodePudding user response:
Apple doesn't reveal exact type-matching table for Objective-C / Swift interoperability, however if you try to convert a Swift class with [Int]
property to Objective-C, you'll end up with the NSArray<NSNumber *> * _Nonnull
type:
class SwiftObject: NSObject {
@objc
var array: [Int] = []
}
Objective-C generated class:
SWIFT_CLASS("_TtC14ObjCPlayground11SwiftObject")
@interface SwiftObject : NSObject
@property (nonatomic, copy) NSArray<NSNumber *> * _Nonnull array;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
Thus I believe your second approach is correct, the interoperability mechanism just cannot do a better job generating a Swift class, because there is no way to inform it that NSNumber
is of int
type inside of an array in Objective-C. As a result you cannot completely rely on the generated code to provide you with the desired method return type. The simplest way to address that is just manually adjust the type in the Swift code:
var x: [Int]
x = some.data().map { $0.intValue }
Another option is to refine the method for Swift implementation and add the converting part manually as an extension. In the Objective-C class method declaration add NS_REFINED_FOR_SWIFT
macro:
@interface TDWSwiftRefinedClass : NSObject
- (NSArray<NSNumber *> * _Nonnull)data NS_REFINED_FOR_SWIFT;
...
And then extend your class somewhere in the Swift code like this:
extension TDWSwiftRefinedClass {
func data() -> [Int] {
__data().map { $0.intValue }
}
}