0

I'm trying to define a class Building which inherits MGLPolygon. MGLPolygon is defined as:

public class MGLPolygon : MGLMultiPoint, MGLOverlay {

    public var interiorPolygons: [MGLPolygon]? { get }

    public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt)

    public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt, interiorPolygons: [MGLPolygon]?)
}

MGLPolygon's designated initializer is hidden in the swift version of SDK. The following would fail:

class Building: MGLPolygon {

    let name: String

    init(name: String, coordinates: [CLLocationCoordinate2D]){
        self.name = name
        super.init(coordinates: &coordinates, count: UInt(coordinates.count))
        // Must call a designated initializer of the superclass 'MGLPolygon'
    }
}

I checked the original SDK code in Objective-C:

@implementation MGLPolygon

@dynamic overlayBounds;

+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
    return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
}

+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
    return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
}

- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
    if (self = [super initWithCoordinates:coords count:count]) {
        if (interiorPolygons.count) {
            _interiorPolygons = interiorPolygons;
        }
    }
    return self;
}

- (mbgl::LinearRing<double>)ring {
    NSUInteger count = self.pointCount;
    CLLocationCoordinate2D *coordinates = self.coordinates;

    mbgl::LinearRing<double> result;
    result.reserve(self.pointCount);
    for (NSUInteger i = 0; i < count; i++) {
        result.push_back(mbgl::Point<double>(coordinates[i].longitude, coordinates[i].latitude));
    }
    return result;
}

- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
    mbgl::Polygon<double> geometry;
    geometry.push_back(self.ring);
    for (MGLPolygon *polygon in self.interiorPolygons) {
        geometry.push_back(polygon.ring);
    }

    mbgl::FillAnnotation annotation { geometry };
    annotation.opacity = [delegate alphaForShapeAnnotation:self];
    annotation.outlineColor = [delegate strokeColorForShapeAnnotation:self];
    annotation.color = [delegate fillColorForPolygonAnnotation:self];

    return annotation;
}

@end

However, unfortunately I'm not good with Objective-C and I don't understand the code.

What am I asking?

What is the designated initializer of MGLPolygon in Swift? What does it take as parameters?

Extra question

Why is the designated initializer hidden?

YOUNG
  • 515
  • 3
  • 13

2 Answers2

0

I think you will need to create a class method in your subclass (e.g. +buildingWithCoordinates:count:) which calls super's implementation in order to handle this.

incanus
  • 5,100
  • 1
  • 13
  • 20
  • I know that. I need to understand how that is exactly done(I don't fully understand Obj-C code). What I figured out from the code above is that even obj-c code doesn't fully expose the implementation. Am I right? – YOUNG Aug 01 '16 at 19:16
  • I am seeing identical problems with Apple's `MKPolygon` as well when bridged to Swift. – incanus Aug 03 '16 at 22:48
  • And this seems to be a known issue: http://stackoverflow.com/questions/5286961/objective-c-sub-classing-basics-how-to-add-custom-property – incanus Aug 03 '16 at 22:59
  • Based on this, I think you are better off creating a protocol for your needs, subclassing `MGLPolygon` without including any initializers, and then making your class conform to your protocol. – incanus Aug 03 '16 at 23:00
0

My solution according to @incanus's advice:

class Building: MGLPolygon {

    var name: String?

    afterInit(name: String){
        self.name = name
    }
}

func initBuilding(name: String, coordinates: [CLLocationCoordinate2D]) -> Building {
    let building = Building(coordinates: &coordinates, count: UInt(coordinates.count))
    building.afterInit(name)
    return building
}     

It's not elegant but it works.

YOUNG
  • 515
  • 3
  • 13