Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift class field of type var Array is converted to immutable NSArray instead of NSMutableArray

A simple Swift class has a field of type var Array. When the class is adapted to Objective-C the field type is exposed as NSArray (immutable) while it should be NSMutableArray (mutable)

class Categoryy: NSObject {

    var items = Array<Item>()

}

The Categoryy swift class is adapted to Objective-C in the Xcode-generated header MODULE_NAME-swift.h file as following:

SWIFT_CLASS("_TtC8waiterio9Categoryy")
@interface Categoryy : NSObject
@property (nonatomic, copy) NSArray * items;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

is there a way for the var items : Array field to be converted to a mutable NSMutableArray in Objective-c?

like image 406
Giorgio Avatar asked Sep 19 '14 09:09

Giorgio


2 Answers

That is because items property is actually immutable.

As you know, because of the nature of struct based implementation of Array in Swift, following code does not affect cat.items.

let cat = Categoryy()

var _items = cat.items
_items.append(item)

Instead, you have to call like this

cat.items.append(item)

On the other hand, in Objective-C, [cat.items addObject:item] is exactly same as following:

id _items = [cat items];
[_items addObject:item];

That is very similar to non-working Swift code.

Sad to say, Objective-C does not have equivalent property/method calling semantics as cat.items.append(item) in Swift.

This is similar to:

// Objective-C
CGRect frame = view.frame;
view.frame = CGRectMake(frame.origin.x, 50, frame.size.width, frame.size.height)

// Swift
view.frame.origin.y = 50

--

Maybe Apple can implement that as NSMutableArray(or private subclass), of course you can request that. I doubt Apple will do that, because that breaks compile time type safety by Generics.

Anyway, I think, you should implement Categoryy like this:

class Categoryy: NSObject {
    var items:[Item] = []

    func addItem(item:Item) {
        items.append(item)
    }

    func setItem(item:Item, atIndex:Int) {
        items[atIndex] = item;
    }

    // and so on ..
}
like image 200
rintaro Avatar answered Sep 28 '22 00:09

rintaro


If you want an NSMutableArray, the easiest way is to just use an NSMutableArray in Swift. Array<T> doesn't (and couldn't) both behave as (an immutable) NSArray and NSMutableArray at the same time - right now, what it does behave as is as an immutable NSArray. Swift could use a built-in class type that corresponds to NSMutableArray the way Array<T> corresponds to NSArray though.

Another alternative is to use a Swift class wrapping an NSMutableArray with @objc exposed methods to access the NSMutableArray. That also lets you use generics to constrain items to T.

like image 40
Jesper Avatar answered Sep 28 '22 00:09

Jesper