Self, protocol extension and non-final class - swift

I tried write a static method for UIView which instantiates view of that class from the nib. Method should be generic and work on every UIView subclasses. Also I want to save the type information – so, for example, in this code
let myView = MyView.loadFromNib()
compiler infers that myView has MyView class. After few trials I decided to use protocol extensions, because otherwise I wouldn't have access to Self inside method body.
Looks like this should work:
protocol NibLoadable {
static func loadFromNib(name: String?) -> Self
}
extension NibLoadable where Self: UIView {
static func loadFromNib(name: String? = nil) -> Self {
let nibName = name ?? "\(self)"
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiateWithOwner(nil, options: nil)[0] as! Self
}
}
extension UIView: NibLoadable {}
But it doesn't. I get compilation error
Method 'loadFromNib' in non-final class 'UIView' must return `Self` to conform to protocol 'NibLoadable'
And two strange things happen. First, if I change protocol declaration to
protocol NibLoadable {
static func loadFromNib(name: String?) -> UIView
}
Everything works just great, including type inference. And second, I can go further and remove where clause altogether:
extension NibLoadable {
...
}
And it keeps working!
So could anyone please explain me why my first variant fails, why second and third work fine and how it's related to final classes?

Here's my understanding of what you are seeing:
You get the compilation error Method 'loadFromNib' in non-final class 'UIView' must return 'Self' to conform to protocol 'NibLoadable' at the point where you declare extension UIView: NibLoadable {}. Let's look at what this statement means to the compiler. It's saying "UIView (and all of its subclasses since it is a non-final class) are adopting the NibLoadable protocol. It means, for UIView, that there will be method with the signature static func loadFromNib(name: String?) -> UIView, because Self in this context is UIView."
But what does this mean for subclasses of UIView? They inherit their conformance and might inherit the implementation of the method from UIView itself. So any subclass of UIView could have the method with the signature static func loadFromNib(name: String? = nil) -> UIView. However, the NibLoadable protocol that all subclasses also conform to says that the return type of that method must be Self. And in the case of any UIView subclass (for example, let's say "MyView"), the return type of the inherited method will be UIView and not MyView. So any subclass would then violate the contract of the protocol. I realize that your protocol extension uses Self and wouldn't create that issue, but technically, you could still also implement the method directly in a UIView extension, and it seems like the Swift compiler just won't allow it at all for that reason. A better implementation might find the Swift compiler verifying that a protocol extension exists which provides the implementation and there is no conflicting inherited implementation, but this appears to just not exist at this time. So for safety, my guess is that the compiler prevents ANY protocols that have methods with Self return types from being adopted by a non-final class. Thus the error you see.
However, making UIView a final class makes that whole inheritance of a non-conforming method possibility and issue go away, which fixes the error.
The reason why changing the return type in the protocol to UIView fixes everything is because not having 'Self' as the return type now relieves the compiler's concern about inherited versions of the method having a non-conforming return type. E.g., if UIView were to implement the method static func loadFromNib(name: String?) -> UIView, and subclasses inherited that method, the protocol contract would still hold for those subclasses, so there is no problem!
On the other hand, the type inference works, because the subclasses of UIView are getting their method implementation from the protocol extension (since the method is not implemented directly in UIView). That implementation returns the type Self, which tells the compiler that the returned value has the same type as the type the method was called on, and the protocol is satisfied, because any subclass of UIView will have a Self type that is a subclass of the required type of UIView.
Removing the where clause works only in this specific case because you changed the protocol method to return UIView, and the protocol extension defines a matching method implementation that returns Self, and then only UIView is getting that extension in your sample code. So the protocol requirement of the method returning UIView matches the implementation UIView is getting which returns Self (which happens to be UIView in this case). But, should you try to make any type other than UIView get the protocol extension method, e.g.
class SomeClass : NibLoadable {}
or even
class MyView:UIView, NibLoadable {}
the compiler wouldn't allow it, because the Self return type in the protocol extension method would not match the UIView required in the protocol. I feel like in the case of "MyView" or other UIView subclasses though, the compiler error might be a bug, since a method that returns MyView would satisfy the protocol requirement that a method return a UIView, if MyView did inherit from UIView.
To summarize some key points:
It doesn't look like the protocol extension has any role in the compiler error you noted. Just this will also create the error:
protocol NibLoadable {
static func loadFromNib(name: String?) -> Self
}
extension UIView: NibLoadable {}
So it looks like the compiler doesn't allow non-final classes to adopt protocols by using default implementations of methods that have a return type of Self, period.
If you change the protocol method signature to return UIView instead of Self that particular compiler warning goes away because there is no longer the possibility of subclasses inheriting a superclass return type and breaking the protocol. And you can then add conformance to the protocol to UIView with your protocol extension. However, you will get a different error if you try to adopt the protocol for any type other than UIView, because the protocol return type of UIView will not match the protocol extension method's return type of Self except in the single case of UIView. This may be a bug, in my opinion, because Self for any subclass of UIView should meet the required UIView return type contract.
But strangely enough, if you adopt the protocol in UIView only, subclasses of UIView will inherit their conformance to the protocol (avoiding the triggering of any of the two above compiler errors) and get their generic implementations from the protocol extension as long as UIView doesn't explicitly implement the protocol method itself. So the subclasses will get the type inference of appropriate Self, and meet the protocol contract for having that method return a UIView.
I'm pretty sure there are one or more bugs mixed up in all this, but someone on the Swift team would have to confirm that to be sure.
UPDATE
Some clarification from the Swift team in this Twitter thread:
https://twitter.com/_danielhall/status/737782965116141568
As suspected, it's a compiler limitation (though apparently not considered an outright bug) that protocol matching does not consider subtypes, only exact type matches. Which is why extension UIView:NibLoadable {} will work when the protocol method defines a return type of UIView, but extension MyView:NibLoadable {} will not.

Use following code should be ok(in Swift 3):
protocol Nibable {}
extension Nibable {
static func loadFromNib() -> Self? {
return Bundle.main.loadNibNamed(String(describing:
type(of:Self.self)), owner: nil, options: nil)?.first as? Self
}
}
final class ViewFromNib: UIView {}
extension ViewFromNib: Nibable {}
var nibView = ViewFromNib.loadFromNib()

Related

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)

Swift Protocol on a specific class?

I am building a UIPresentationController subclass. UIPresentationController defines an default initializer like this:
init(presentedViewController: UIViewController , presentingViewController: UIViewController?)
Now, in order for this to work, I want my presentedViewController to conform to some protocol, say MyRandomProtocol.
How can I re-write my initializer such that it takes the first argument as both a UIViewController subclass, and one that specifically conforms to MyRandomProtocol?
You can use generics.
init<T: UIViewController>(presentedViewController: T, ...) where T: MyRandomProtocol {
//initialization code
}

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().

Why can the keyword "weak" only be applied to class and class-bound protocol types

When I'm declaring variables as weak in Swift, I sometimes get the error message from Xcode:
'weak' may only be applied to class and class-bound protocol types
or
'weak' must not be applied to non-class-bound 'SomeProtocol'; consider adding a protocol conformance that has a class bound
I'm wondering why the keyword weak can only applied to class and class-bound protocol types? What is the reason behind this requirement?
One common reason for this error is that you have declared you own protocol, but forgot to inherit from AnyObject:
protocol PenguinDelegate: AnyObject {
func userDidTapThePenguin()
}
class MyViewController: UIViewController {
weak var delegate: PenguinDelegate?
}
The code above will give you the error if you forget to inherit from AnyObject. The reason being that weak only makes sense for reference types (classes). So you make the compiler less nervous by clearly stating that the PenguinDelegate is intended for classes, and not value types.
weak is a qualifier for reference types (as opposed to value types, such as structs and built-in value types).
Reference types let you have multiple references to the same object. The object gets deallocated when the last strong reference stops referencing it (weak references do not count).
Value types, on the other hand, are assigned by copy. Reference counting does not apply, so weak modifier does not make sense with them.
protocol PenguinDelegate: class {
func userDidTapThePenguin()
}
class MyViewController: UIViewController {
weak var delegate: PenguinDelegate?
}
If you type class after your protocol it works as well and seems more appropriate that for NSObjectProtocol.
Well just in case anyone else thinks that you have everything correct in your code like me, check that you did not mistakenly replaced the : by an =.
Here is what I had. It was also giving me the same error as above:
protocol PenguinDelegate: class {
func userDidTapThePenguin()
}
class MyViewController: UIViewController {
weak var delegate = PenguinDelegate?
}
But the correct way is:
protocol PenguinDelegate: class {
func userDidTapThePenguin()
}
class MyViewController: UIViewController {
weak var delegate: PenguinDelegate?
}
Do you see the difference? It took me a while to see that I had an equal sign instead of a colon. Also note that I did get other errors for the same line for I had decided my first error seem like the most likely to be the real problem :
-weak may only be applied to class and class-bound protocol types
:-<
I find out in one case where you even have class type but still you get this error message.
For example,
class MyVC: UIViewController {
var myText: UITextView = {
[weak self]
let text = UITextView()
// some codes using self
return text
}()
}
Here an UITextView object is returned from an anonymous block as initialization of var myText. I got the same type of error message. To resolve the issue, the var has to be marked as lazy:
class MyVC: UIViewController {
lasy var myText: UITextView = {
[weak self]
let text = UITextView()
// some codes using self
return text
}()
}
Just FYI and who is not updated.
After swift proposal SE-0156 https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md was implemented, there is in the Swift docs "Class-Only Protocols section" https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID281 now described to use AnyObject instead of class.
So, it is possible for : class to be deprecated in future.
weak is for ARC(Automatic Reference Counting). It means not adding reference count. So it only works for Class. And in Swift, you will get optional value for security.
I tried to capture String and Array-typed properties for a closure. I got these errors:
'weak' may only be applied to class and class-bound protocol types, not '[String]'
'weak' may only be applied to class and class-bound protocol types, not 'String'
I played a while in the playground, and it turned out, capturing self is enough for these types.
I was using objective C class in swift for a scrolView. I created IBOutlet of that scroll view. And while compiling code this error started showing.
So to fix this kind of issue, import that class in your bridging header
import "YourClass.h"
I was using Xcode 9.2 with swift 3.2
weak only works for reference type, so Xcode would report an error if you are calling from struct (instead of class).
weak is not for value type.
weak comes to the picture only for the class.
"weak" can apply anything which is inherited from class or class-bound protocol types
Class Protocol:
protocol ViewControllerDelegate : class {
func getInformationk(value: String?) }
NSObjectProtocol:
protocol ViewControllerDelegate : NSObjectProtocol {
func getInformation(value: String?) }

Swift Generic is treated as a parent in typealias

I have a method:
public func someMethod<Controller>(controller: Controller) {
print(controller)
typealias HandlerType = Controller -> UIViewController
let handler: HandlerType
print(handler.dynamicType)
}
After calling it with the instance of child of UIViewController, which is ViewController in my case, it prints:
<Test.ViewController: 0x7fc43be479d0>
UIViewController -> UIViewController
What is my goal is to have a typealias like this:
Test.ViewController -> UIViewController
I have typealias and the declared valriable:
public typealias ControllerType = UIViewController
public private(set) static var visibleController: ControllerType?
And the method invocation is:
if let controller = visibleController {
someMethod(controller)
}
I do not understand why ViewController becomes UIViewController in typealias, so if anyone faced with such kind of an issue, and/or have a solution, Please let me know.
Thank you in advance.
This is due to the fact that the static and dynamic type of your controller differ.
The static type is taken from your ControllerType typealias, which you've defined to be UIViewController. The dynamic type is the same type as the instance that visibleController points to – in this case Test.ViewController.
Because generics are inferred from the static type that you pass into the function (their specialisation takes place at compile-time), Controller will be inferred to be UIViewController. Therefore your typealias will be UIViewController -> UIViewController, and that's what dynamicType of your handler will return (it appears to fallback to the static type when unassigned).
On the other hand, the dynamic type of your controller will indeed be Test.ViewController, as that's the dynamicType of the instance that you pass in.
As for solutions, without knowing exactly what you're trying to achieve here, it's hard to say.