Bronze challenge: method not available for view but for webView

#1

Using Swift 3 in Xcode 8 now. Here is the code I have for displaying the webView.

override func loadView() {
webView = WKWebView()

    // set it as *the* view of this view controller
    view = webView
    
    let url = URL(string: "https://www.bignerdranch.com")
    let urlRequest = URLRequest(url: url!)
    //view.load(urlRequest)  // static member load cannot be used on instance of type UIView
    webView.load(urlRequest)

What is confusing me is that although I have set view to be webView, I cannot call the load method with view but I can with webView. The error reported by Xcode is that the static member load cannot be used on instance of type UIView (which view is). So what is the point of setting view to be webView if it can’t take the methods that webView can? I guess I want to know if this is a but or am I misunderstanding the reason for setting view to be webView. In MapViewController we set view to be mapView, so that is where I got the idea from.

#2

Okay, I can see that without setting view to be webView, the view would just be a black screen. So that line is important. So should we do all configurations from that point with webView as in webView.load(urlRequest)?

#3

I think I am getting more comfortable with what happened. The discussion at statically typed helps me see what might be going on. And from what I’ve read, Swift is dynamically typed.

#4
class Animal {
    static func speak() {
        print("Animal, animal")
    }
}

class Dog:Animal {
     func speak() {
        print("Bark, bark!")
    }
}

var myAnimal: Animal
myAnimal = Dog()
myAnimal.speak()

--output:--
Playground execution failed: /var/folders/th/8j21gcbs5757m9q728jrlvxw0000gn/T/./lldb/459/playground38.swift:15:1: 
error: static member 'speak' cannot be used on instance of type 'Animal'
myAnimal.speak()
^~~~~~~~ ~~~~~

Static functions can only be called on the class, for instance:

Animal.speak()

--output:--
Animal, animal

I guess I want to know if this is a but

What is a “but”?

#5

Oops. I meant a bug, not a but.

In the example I gave, view and and webView are instances (of UIView and WKWebView, respectively). The method load is also an instance method of objects of type WKWebView. The problem is that even though view references an object of type WKWebView, it is declared as an object of type UIView. The class UIView does not have the instance method load.

So I think the issue of a class (or type) method, as opposed to an instance method, as in the example you gave, is a different situation. However, I don’t know why the error mentions static member. Maybe static member in the warning doesn’t mean class (type) method.

#6

I do not believe it is. The variable type is UIView, therefore methods are looked up in the UIView class. The error message is saying(by implication) that the UIView class has no instance method named load(), but that UIView does have a static method named load(), and you cannot call the static load() method on the instance stored in the UIView variable. If you need to call WKWebView methods on an instance stored in a variable of type UIView, then you first have to cast the instance to the type WKWebView, e.g.

let x = webView as! WKWebView
x.load(...)

After casting, x will be of type WKWebView, so methods called on x will be looked up in the WKWebView class.

If you look at the Apple docs for UIView, they do not list a load() method, however UIView inherits from NSObject, and NSObject defines a static load() method:

class func load()

In Swift, you can also use the class keyword to define a static function. That is the static load() method that your code is attempting to call on an instance–hence the error message.

#7

I see. I didn’t check the superclass’s methods. What I don’t understand is why doesn’t the error say something about class method (or member) as opposed to saying static member. It seems there are some differences between the two, although I am still trying to understand when to use which.

#8

To test out what you were saying, I tried to use some of the class methods of NSObject on UIViewController. The only way I got it to work was by using UIViewController.self as in UIViewController.self.load or UIViewController.self.superclass(). In the docs, where it discusses methods and in particular type methods, it gives the following example:

class SomeClass {
    class fun someTypeMethod() {
        // type method implementation goes here
   } 
}
someClass.someTypeMethod()

The point is that they don’t use self to call the type method. The class method is defined right in the class as opposed to a superclass. I found one class method in UIViewController and it actually works with and without .self:

UIViewController.attemptRotationToDeviceOrientation()

So as far as I can tell, if the class method is defined in the class, you don’t have to refer to self when calling the method. But otherwise, you do. Why is that?

#9

I’m not seeing that. The following executes without error in Swift 2.2:

import UIKit

UIViewController.load()
print("hello")

--output:--
hello

ClassName.self returns what’s known as the meta-type object of the class–something you can read about if you are interested.

So as far as I can tell, if the class method is defined in the class, you don’t have to refer to self when calling the method. But otherwise, you do. Why is that?

I’m not seeing that either. I can do this:

class Animal {
    static func speak() {
        print("I'm an Animal!")
    }
}

class Dog: Animal {
}

Dog.speak()

--output:--
I'm an Animal!

Or, I can do this:

class Animal {
}

class Dog: Animal {
    static func speak() {
        print("I'm an Animal!")
    }
}

Dog.speak()

--output:--
I'm an Animal!

What I don’t understand is why doesn’t the error say something about class method (or member) as opposed to saying static member.

Swift is a recent language. There is a long history of other programming languages. Most languages don’t try to invent new terms for old concepts, such as static or class methods. The person who wrote the error messages used common place terminology. However, Apple’s own guide uses the term type method for such methods, so to be consistent the error message should say type method as well. And to be totally consistent, the keyword you use in your code should be type instead of static/class, e.g. type func doStuff() {...}.

class method (or member) as opposed to saying static member. It seems there are some differences between the two, although I am still trying to understand when to use which.

I suggest you use static func as your default. If for some reason, you need to let a subclass override a static method, then change to class func. Computer programming languages have lots of features that you may never use–or even learn about.

#10

Thanks for sharing. I can’t repeat the behavior I had yesterday that lead me to think you needed self. I will look into the meta type object. I did run into this again:

print("Is UIViewController as subclass of UIResponder: \(UIViewController.isSubclass(of: UIResponder.self))")

I get an error without .self on the argument UIResponder that says expected member name or constructor call after type name. I am not sure why that error shows up.

#11

Because every function requires that you call it with the proper argument types. isSubclass(of:) requires an argument of type AnyClass. Is UIResponder of type AnyClass? (Hint: Xcode is telling you: “no”.)

See: http://en.swifter.tips/self-anyclass/

#12

Thanks again. Nice article. I didn’t know about AnyClass. I probably thought it was AnyObject.