Wladimir Feldman Wladimir Feldman - 3 months ago 10
Swift Question

Class property assignment is not the same as assignment to properties required by protocol (example inside)?

A week ago I started to learn OS X programming with Swift. I've decided to start with creating a simple two-panel file manager to learn language basics and API. But now I've found a bit of strange behavior that I can not neither understand nor find an explanation in docs to. Let he explain it by example.

I have a custom ViewController class SplitViewController that represents main two-panel part of the file manager's window. To let it communicate with underlying View Controllers I also extend it with FileManagementDelegate protocol which defines some useful methods.

I also have a OnePanelViewController class that is meant to manage each of the file panels. It is also extended by a protocol, at this time FilePanelController one, again for inter-controller communication needs. This protocol is defined as:

protocol FilePanelController {
var delegate: FileManagementDelegate? {get set}
var ID: Int? {get set}
func getSelectedFiles() -> [NSURL]
func getFocus() -> Bool
func gotFocus()

}


In viewDidAppear() method of the SplitViewController class I need to set running instance as delegate for both file panels. I tried to do it this way:

for controller in self.childViewControllers{
if controller is FilePanelController {
(controller as! FilePanelController).delegate = self
}
}


But that leads to compile-time error: "Cannot assign to immutable expression of type 'FileManagementDelegate?'"

I tried to change code to:

for var controller in self.childViewControllers{...


But it didn't work either.

At the same time if I change it to:

for controller in self.childViewControllers{
if controller is FilePanelController {
(controller as! OnePanelViewController).delegate = self
}
}


then everything works fine.

To access delegate property as a property of FilePanelController protocol and not of OnePanelViewController class I have to do it this way:

for controller in self.childViewControllers{
if controller is FilePanelController {
var fpController: FilePanelController = (controller as! FilePanelController)
fpController.delegate = self
}
}


that looks a bit awkward.

I can not understand why? It is the same property of the same class. What is the problem here I do not see?

Answer

Protocols in Swift can by apply to class and structs. Complier don't know which are you using so he restrict it.

You can define your protocol to by applicable only for classes:

protocol FilePanelController: class { ... }

or by using conditional casting:

for controller in self. childViewControllers {
    if var filePanelViewController = controller as? FilePanelController {
        filetPanelViewController.delegate = self
    }
}

When you use OnePanelViewController as cast type, complier infer that it is class.