Home > Blockchain >  How to bridge an array of integer from Objective-C to Swift?
How to bridge an array of integer from Objective-C to Swift?

Time:09-06

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 }
    }
    
}
  • Related