Cast needed in subclassed objc array with fixed return type in Swift

159 Views Asked by At

I have a subclass of NSArray, array of labels:

@interface LabelArray : NSArray
  - (UILabel*)objectAtIndex:(NSUInteger)index;
  - (UILabel*)objectAtIndexedSubscript:(NSUInteger)index;
@end

@interface ViewController : UIViewController
  @property (nonatomic,readonly) LabelArray* labels;
@end

When I try to access it from Swift code with 0 index, everything goes fine:

someObject!.labels[0].textColor = UIColor.redColor()

But when I use an Index variable

var Index: Int = 0
someObject!.labels[Index].textColor = UIColor.redColor()

xcode gives an error: "Could not find member 'textColor'" and forces me to use an ugly code like this:

(someObject!.labels[Index] as! UILabel).textColor = UIColor.redColor()

I'm subclassing the array to be able to modify a group of labels at once, like labels.textColor = UIColor.redColor() will modify each label in array.

What am I doing wrong, and is there a way to avoid this ugly cast?

1

There are 1 best solutions below

0
On

Well, I found an answer.

The problem is in Swift strong typing system.

The argument type in overridden objectAtIndex: is Unsigned Integer, and Index in the caller procedure was declared as Integer. That's why compiler was considering that Swift Array's objectAtIndex: procedure with Int argument, returning the AnyObject type, was used.

Bridged declaration looks like this: func objectAtIndex(index: Int) -> AnyObject

The solution is to redeclare objectAtIndexedSubscript: with (NSInteger)index instead of NSUInteger

OR to use UInt() conversion: someObject!.labels[UInt(Index)].textColor = ...

We can also overload the subscript property (overload the square brackets operator), but we cannot directly extend the LabelArray, because subscript will conflict with obj-c objectAtIndexedSubscript method.

Instead, we can declare a protocol, extend it with a new method, and then extend the LabelArray with this protocol:

protocol LabelArrayProtocol { }

extension LabelArrayProtocol {
  subscript(index: Int) -> UILabel {
    return (self as! NSArray).objectAtIndex(index) as! UILabel
  }
}

extension LabelArray: LabelArrayProtocol { }