What is the designated initializer for a MKPolygon in Swift4? - swift4

I'm trying to set up a class that inherits from MKPolygon as below, but the compiler rejects my call to super.init() with the following error message:
Must call a designated initializer of the superclass 'MKPolygon'
What is the designated initializer for MKPolygon?
Following the advice of this answer and this one, I've scoured the class documentation. There are four initializers available but all of them are declared as Convenience Initializers.
init(points: UnsafePointer, count: Int)
init(points: UnsafePointer, count: Int, interiorPolygons: [MKPolygon]?)
init(coordinates: UnsafePointer, count: Int)
init(coordinates: UnsafePointer, count: Int, interiorPolygons: [MKPolygon]?)
I'm fairly new to Swift so I'm sure there's something simple I'm missing.
My subclass implementation is below, in case that helps.
import MapKit
import UIKit
class AudioGuideRegionAnnotation: MKPolygon {
// MARK: - Properties
let color: UIColor
let image: UIImage?
// MARK: - Initializers
init(region: AudioGuideRegion) {
var locations = region.locations
super.init(coordinates: &locations, count: locations.count) // <-- rejected with "Must call a designated initializer of the superclass 'MKPolygon'"
self.title = region.title
self.subtitle = "\(Int(round(Double(region.duration / 60)))) minutes"
self.color = .black
self.image = region.images.first?.image
super.init()
}
}

The real answer to this specific issue is that you can subclass them, but your subclass must not require using its own initializer. Instead you must continue to use the convenience initializers in the super classes. Those initializers must be called otherwise MapKit won't render the data.
For example with MKPolyline:
let mine = MyPolyline(coordinates: c, count c.count)
mine.other = ...
mine.foobar = ...
class MyPolyline: MKPolyline {
var foobar: [Int] = []
var other: [Double] = []
}
You might think you could add your own initializer and simply override methods like points and pointCount (similar to what you would for NSString for example), but MapKit still won't render the data unless its own "convenience"-marked initializers were called.

Following Matt's helpful comment above, it turns out the answer is that MKPolygon and several other classes do not have accessible designated initializers.
In such a case subclassing is not possible, and the only course of action appears to be to extend the class you intended to subclass.
This answer, linked to by Matt is supremely helpful as further reading.

Related

Should I use a bridge class for setting my delegates for my ViewController?

I can simply use delegates in my views or viewControllers, no issue there, I though that would be nice if I use a bridge class for my entire app for more control on delegates, like this:
protocol MyDelegate {
func work1()
func work2(value: Int)
func work3(value: String)
}
class MyClass {
lazy var work1Delegate: MyDelegate? = nil
lazy var work2Delegate: MyDelegate? = nil
lazy var work3Delegate: MyDelegate? = nil
}
let sharedMyClass: MyClass = MyClass()
And were I need to take action I simply use this on viewDidLoad and also conforming to MyDelegate as well:
sharedMyClass.work1Delegate = self
sharedMyClass.work2Delegate = self
sharedMyClass.work3Delegate = self
I found 2 downside in my method, first I have to type 3 line of codes like this for example for my ViewController, in the real app it would be like 10 function and 10 lines like this:
sharedMyClass.work1Delegate = self
sharedMyClass.work2Delegate = self
sharedMyClass.work3Delegate = self
And second if I just need func work2(value: Int) for another view or viewController I have to have those other 2 function as well because of conforming to my protocol.
I wanted show what I am doing and asking for solving this 2 issues, having a bridge class is a must for me, but I would like to solve those 2 minor issues.

Cannot cast value of type MyClass to MyDelegate

I have a class that needs to set a component to self. That component requires the class to implement a protocol MyDelegate. Eventually, it fails (SIGNAL SIGABRT).
// I need the class to be a NSObject for unrelated requirements
class MyClass: NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyDelegate: self as! MyDelegate)
}
// in the same file
extension MyClass: MyDelegate {
func myUsefulDelegateCall() {
}
}
Why?
The problem is the usage of self inside the stored property myComponent.
Typically, it is not allowed to hand out self before the initializer has finished intializing the whole object. Therefore, your problem has nothing to do with protocols or extensions. More simple:
import Foundation
class Component {
init(requiresAnObjectofTypeMyClass:MyClass) {
}
}
// I need the class to be a NSObject for unrelated requirements
class MyClass : NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
}
let m = MyClass()
also crashes.
If you leave out the NSObject subclassing, you get a compiler error:
use of unresolved identifier 'self'
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
This shows the problem: You must not use self here.
I think it's just an Xcode bug; Xcode seems to ignore the syntax error when subclassing NSObject. The cast as! MyClass is also a hint that we are looking for a strange workaround that finally get's Xcode to it's knees and causes the runtime crash.
To work-around, you could create a lazy property, which will be evaluated after the initialization process and therefore will allow self to be handed into the Component initializer:
private(set) lazy var myComponent = Component(requiresAnObjectofTypeMyClass:self)
Here, you also do not need the cast. Unfortunately, lazy let is not allowed in swift (and nobody knows why), so private(set) is close to it's semantic.
It's easy to transfer this code to your protocol example.
A type cast is not needed. As MyClass adopts MyDelegate it is also MyDelegate.
And initialize the property lazily to be able to use self on the top level at all.
private lazy var myComponent = Component(requiresAnObjectofTypeMyDelegate: self)

why do I get "Attempted to unregister unknown __weak variable" when copying an instance variable?

I noticed this today when playing with NSOutlineView and NSTableHeaderCell, but when this specific configuration is made, an error/warning(?) is printed:
objc[2774]: Attempted to unregister unknown __weak variable at 0x1016070d0. This is probably incorrect use of objc_storeWeak() and objc_loadWeak(). Break on objc_weak_error to debug.
here's the snippet:
class Foo: NSCell {
weak var weak: NSView?
override func copy(with zone: NSZone? = nil) -> Any {
// according to NSCopying documentation:
// If a subclass inherits NSCopying from its superclass and declares
// additional instance variables, the subclass has to override copy(with:)
// to properly handle its own instance variables, invoking the superclass’s implementation first.
let copy = super.copy(with: zone) as! Foo
// this produces "Attempted to unregister unknown __weak variable"
copy.weak = self.weak
return copy
}
}
let view = NSView(frame: NSRect.zero)
let foo = Foo()
foo.weak = view
let copy = foo.copy() as! Foo
this also happens if I substitute NSCell with: NSEvent, NSImage, NSImageCell
but this doesn't happen to NSColor, NSDate, NSIndexPath
I started learning Swift without prior knowledge of Obj-C. could someone help me understand why this is? is it safe to ignore? who has the blame in this case?
This is a framework bug. It's easy to reproduce with the following crasher:
import Cocoa
class Cell: NSCell {
var contents: NSString?
override func copy(with zone: NSZone? = nil) -> Any {
let newObject = super.copy(with: zone) as! Cell
newObject.contents = contents
return newObject
}
}
func crash() {
let cell = Cell()
cell.contents = "hello world"
cell.copy() // crashes while releasing the copied object
}
crash()
When you use a weak var instead, you get the error message that you showed.
My gut feeling is that there is something in the copy implementation of NSCell (and possibly of NSEvent and NSImage) that does not handle subclassing for types that have non-trivial constructors. Accordingly, if you change let newObject = super.copy(...) with let newObject = Cell(), the crash is avoided. If your superclass's copy logic is simple enough, you should probably do that for now.
If you hit this problem, you should file a bug report separately of mine, but you can probably reuse my sample.

The type of self in Swift and its use with respect to two-phase initialization

Consider the following code, which adds a gesture recognizer to a view.
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleGesture(gesture:)))
let test1 = self
#objc func handleGesture(gesture: UITapGestureRecognizer) {
// some code
print("hello")
}
override func viewDidLoad() {
let test2 = self
super.viewDidLoad()
imageView.addGestureRecognizer(gesture)
}
}
As per this question, the above code does not work because I'm trying to use self (in the gesture recognizer's initializer) when not fully initialized, and this is so because of Swift's two-phase initialization.
I'm not interested in the easy fix to make this work, but this triggers a couple of questions:
1) Why does the compiler allow us to use self here if self is not ready to be used? Shouldn't I get a compiler error if I'm trying to use self too soon?
2) We can't directly inspect the type of self with alt+click in XCode. However, we can inspect the types of my ad hoc variables test1 and test2. While test2's type is ViewController, as expected, test1's type is (ViewController) -> () -> ViewController (i.e., a closure that takes a ViewController and returns a closure that takes nothing and returns a ViewController). What is that and why does self have two different types within the same class?
1)
Shouldn't I get a compiler error if I'm trying to use self too soon?
I do agree. You may send a bug report to swift.org.
Why does the compiler allow us to use self here if self is not ready to be used?
Unfortunately, there's another self in the descendants of NSObject, the method self() of NSObject.
2)
What is that and why does self have two different types within the same class?
The current Swift interprets the initial value expression in the class context, not in the instance context.
You know method names can be used as closures in Swift:
class ViewController: UIViewController {
//..
func aMethod() {
//...
}
func anInstanceMethod() {
let meth = aMethod // () -> ()
}
}
Swift can also refer to an instance method in the class context, which generates a so-called unapplied method reference (see SE-0042), which currently returns a curried function:
class ViewController: UIViewController {
//...
func aMethod() {
//...
}
class func aClassMethod() {
let meth = aMethod // (ViewController) -> () -> ()
}
}
The method self() as well.
Generally we do not need self() method and this behavior should be changed, I think.
This is interesting behaviour that works for Objective-C objects. Let's take these three examples:
class Object: NSObject {
let test = self // compiles
}
class NonNSObject {
// let test = self // errors
lazy var lazyTest = self // compiles
}
struct NonClass {
// let test = self // errors
lazy var lazyTest = self // errors
}
NonNSObject exhibits what you'd escape:
The object cannot reference itself until it is fully initialized, and let bindings must all be initialized before full initialization, so this failed.
However, NSObject happens to have an Objective-C method - (instancetype)self; which returns self. We can model this on NonNSObject as so:
func returnSelf() -> NonNSObject {
return self
}
This is where we start to see the answer to 2).
If we reference this method returnSelf on the Class we get the signature (NonNSObject) -> () -> NonNSObject. You can do this with any instance method as so:
let test = NonNSObject.returnSelf
The signature makes sense in this context:
The argument is the object we actually want to call the method on
Then we "apply" the function (with no arguments, in this case)
And we finally get our return value
let curriedFunction = NonNSObject.returnSelf // (Self) -> () -> Self
let readyToCall = curriedFunction(NonNSObject()) // () -> Self
let finallyApplied = readyToCall() // Self
Putting all the pieces together, we can see that in the case of ViewController (which inherits from UIViewController which way up the chain inherits from NSObject) there is an instance method self which the compiler is assuming you meant, so it uses that instead of the instance itself (as that would be an error). Its signature is thus a natural consequence of using an instance method on the class itself—it needs an instance, which is the first argument.
In summary:
1) Instead of assuming you made an error, the Swift compiler finds a function self on NSObject and returns the curried form.
2) This is the curried form of a function, in particular, an instance method which returns its own type.
2.5) It's still highlighted in pink because Swift-ObjC interop is mildly hacky, and self is both a method and, well, self.
As a bonus, the struct cannot reference itself at all, even lazily.

UIView, CMDeviceMotionHandler : unowned may only be applied to class and class-bound protocol types

I'm creating a UIView that listens to CMDeviceMotion Events:
class MyView: UIView{
private var motionManager = CMMotionManager()
let motionQueue = NSOperationQueue()
override func awakeFromNib() {
self.setupView()
}
func setupView(){
self.motionManager.deviceMotionUpdateInterval = 0.5
self.motionManager.startDeviceMotionUpdatesUsingReferenceFrame(.XArbitraryZVertical, toQueue: self.motionQueue, withHandler: self.motionHandler)
}
// MARK: - CMDeviceMotionHandler
let motionHandler : CMDeviceMotionHandler = {
[unowned self] (motion,error) in
}
}
I'd like to declare my CMDeviceMotionHandler closure as a member variable however I get the error:
'unowned' may only be applied to class and class-bound protocol types,
not 'MyView -> () -> MyView'
MyView is a UIView which in turn is a class so I don't get why it's complaining that unowned can not be applied.
I've searched for other questions with the same issue but most of them dealt with lazily computed variables. How do I resolve this error for my scenario?
The line of code you're on is actually run during the init function. self is not available until after all stored properties are initialized. You're in the middle of the process.
The error message is quite confusing and useless, because self in the context of property initializers is not an instance of a MyView, but a tricky meta-type: a class-member function that is unbound to its instance, but becomes bound and usable once the instance is passed in as the first argument. It's to do with member functions being implemented in Swift with currying, and is rather academic unless you love type calculus.
You have two options:
Declare it indeed as lazy var instead of let, so the code is not run during init but in fact at first use.
Declare it without initialization as an Optional. Depending on your design constraints this is either cumbersome or elegant. No way to know. Anyway, before it is needed, initialize it to a non-nil value. An easy place to do this, if this UIView is used strictly within Storyboard, is to initialize it within awakeFromNib().