17

In Objective-C you can require a class and additional protocol implementations for properties:

@property (nonatomic) UIViewController<UISplitViewDelegate> *viewController;

Is this possible in Swift? From the documentation it looks like you can only require either a class or a protocol.

karthikPrabhu Alagu
  • 3,371
  • 1
  • 21
  • 25
lassej
  • 6,256
  • 6
  • 26
  • 34
  • duplicate to http://stackoverflow.com/questions/26474061/whats-the-swift-equivalent-of-declaring-typedef-someclasssomeprotocol-mytype – Bryan Chen Jan 06 '15 at 03:29

5 Answers5

16

There are actually two of ways to achieve this in Swift:

  1. Using an empty "phantom" protocol. Create an empty protocol and make UIViewController conform to it. This is the most "Swift" method, it's safe and it's dynamic (doesn't require specifying a class at compile-time).

    protocol _UIViewControllerType {}
    extension UIViewController: _UIViewControllerType {}
    
    class MyClass {
        weak var viewController: protocol<UISplitViewControllerDelegate, _UIViewControllerType>?
    }
    

    You can also declare a typealias for this type (just to reduce the code mess).

    class MyClass {
        typealias ViewControllerType = protocol<UISplitViewControllerDelegate, _UIViewControllerType>
        weak var viewController: ViewControllerType?
    }
    
  2. Using generic constraints. As mentioned by fnc12 and Konstantin Koval. This is safe, but doesn't allow you to "swap" the view controller instance at run-time.

    class MyClass<T: UIViewController where T: UISplitViewControllerDelegate> {
        weak var viewController: T?
    }
    

I hope the next Swift release adds a way to specify both constraints without using a "phantom protocol"...

typealias ViewControllerType = UIViewController: UISplitViewControllerDelegate // wish
Community
  • 1
  • 1
akashivskyy
  • 44,342
  • 16
  • 106
  • 116
  • Thanks for the answer, but solution 1 doesn't allow me to use any UIViewController methods without casting, right? E.g. `viewController.title = "Some Title"`. Or use it as a method parameter where a UIViewController is required? E.g. `otherViewController.addChildViewController( viewController)`. – lassej Jan 09 '15 at 15:27
  • Yes. You would have to add an extra `let viewController = object as UIViewController` line every time you want to use its API. – akashivskyy Jan 09 '15 at 15:45
  • I still think this is a small cost. – akashivskyy Jan 09 '15 at 15:47
  • Well, for me not having to cast is the whole point. Otherwise I could just declare the property as AnyObject. – lassej Jan 09 '15 at 16:08
  • Then you could get a runtime fatal error, as this could be an instace of `CMMotionActivityManager`. Using a protocol at least ensures that it's not *literally* any object. – akashivskyy Jan 09 '15 at 16:25
  • 1
    If you want to avoid the cast entirely, the second option might be more suitable. There is no `XYZClass` syntax equivalent in Swift (as of today). – akashivskyy Jan 09 '15 at 16:26
  • Wow, this feels so wrong... Thank you so much for this workaround. :) – JRG-Developer May 29 '15 at 16:37
4

Yes you can do that

class A < T : SomeClass where T: Comparable> {
    var myProperty: T
    init(t :T) {
        myProperty = t
    }
}

Declare class A that has a property of type T. T is SomeClass or subclass and it has to adopt Comparable protocol

When declaring a property you can use protocol as type

class MyClass {
    var nsobject: NSObjectProtocol
    init(object : NSObjectProtocol) {
        nsobject = object
    }
}

// Pure Swift
protocol RandomNumberGenerator {
}

class Dice {
    let generator: RandomNumberGenerator
    //specify many protocols
    let printer: protocol<Printable, NicePrintable> 
}

You can read documentation here

Kostiantyn Koval
  • 8,407
  • 1
  • 45
  • 58
  • So how would I specify a property that is required to be a UIViewController and implements the UISplitViewDelegate protocol? – lassej Jan 06 '15 at 10:56
4

Same as @akashivskyy answer, using empty "phantom" protocol
But here i am doing it as separate class which implements that protocol - MyViewController, which can be used as type for var declaration. Which simplified in my implementaion.

@objc protocol MySplitViewControllerDelegate : NSObjectProtocol {
    func controllerTitle() -> String
    optional func mySplitView() // write delegates
}

class MyViewController: UIViewController, MySplitViewControllerDelegate {
    func controllerTitle() -> String {
        return ""
    }
}

class ViewController: UIViewController {

    private(set) weak var viewController: MyViewController?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
3

In Swift 4 you can do this by:

let viewController: UIViewController & UISplitViewDelegate
Jeroen Bakker
  • 2,142
  • 19
  • 22
2

This is where generics are needed.

@property (nonatomic) UIViewController<UISplitViewDelegate> *viewController;

Assume you want class called 'MyClass" to have a property called 'viewController' with type UIViewController (or subclass) and that conforms UISplitViewDelegate protocol. In Swift your code will look like

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject{
    var viewController:T?

    override init(){
        super.init()
        //..
    }

    //  etc..
}

Notice

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject

line. Here you specify a random T type but you also specify you want T to be derived from UIViewController and conform UISplitViewControllerDelegate. This condition will be checked during compilation. And you declare a property in this line

var viewController:T?

and specify its type as T. And one question left - how to declare variable of type MyClass? I provide a minimum code from a sample project to illustrate more clearly.

class MyClass<T:UIViewController where T:UISplitViewControllerDelegate>:NSObject{
    var viewController:T?

    override init(){
        super.init()
        //..
    }

    //  etc..
}

class ViewController: UIViewController,UISplitViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var a=MyClass <ViewController> ()
        a.viewController=self
        //..
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //..
}

More information here

Good luck with generics.

fnc12
  • 2,241
  • 1
  • 21
  • 27