Class conforming to protocol as function parameter in Swift - swift

In Objective-C, it's possible to specify a class conforming to a protocol as a method parameter. For example, I could have a method that only allows a UIViewController that conforms to UITableViewDataSource:
- (void)foo:(UIViewController<UITableViewDataSource> *)vc;
I can't find a way to do this in Swift (perhaps it's not possible yet). You can specify multiple protocols using func foo(obj: protocol<P1, P2>), but how do you require that the object is of a particular class as well?

You can define foo as a generic function and use type constraints to require both a class and a protocol.
Swift 4
func foo<T: UIViewController & UITableViewDataSource>(vc: T) {
.....
}
Swift 3 (works for Swift 4 also)
func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource {
....
}
Swift 2
func foo<T: UIViewController where T: UITableViewDataSource>(vc: T) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}

In Swift 4 you can achieve this with the new & sign:
let vc: UIViewController & UITableViewDataSource

The Swift book documentation suggests that you use type constraints with a where clause:
func someFunction<C1: SomeClass where C1:SomeProtocol>(inParam: C1) {}
This guarantees that "inParam" is of type "SomeClass" with a condition that it also adheres to "SomeProtocol". You even have the power to specify multiple where clauses delimited by a comma:
func itemsMatch<C1: SomeProtocol, C2: SomeProtocol where C1.ItemType == C2.ItemType, C1.ItemType: SomeOtherProtocol>(foo: C1, bar: C2) -> Bool { return true }

Swift 5:
func foo(vc: UIViewController & UITableViewDataSource) {
...
}
So essentially Jeroen's answer above.

With Swift 3, you can do the following:
func foo(_ dataSource: UITableViewDataSource) {
self.tableView.dataSource = dataSource
}
func foo(_ delegateAndDataSource: UITableViewDelegate & UITableViewDataSource) {
//Whatever
}

What about this way?:
protocol MyProtocol {
func getTableViewDataSource() -> UITableViewDataSource
func getViewController() -> UIViewController
}
class MyVC : UIViewController, UITableViewDataSource, MyProtocol {
// ...
func getTableViewDataSource() -> UITableViewDataSource {
return self
}
func getViewController() -> UIViewController {
return self
}
}
func foo(_ vc:MyProtocol) {
vc.getTableViewDataSource() // working with UITableViewDataSource stuff
vc.getViewController() // working with UIViewController stuff
}

Update for Swift 5:
func yourFun<V: YourClass>(controller: V) where V: YourProtocol

Note in September 2015: This was an observation in the early days of Swift.
It seems to be impossible. Apple has this annoyance in some of their APIs as well. Here is one example from a newly introduced class in iOS 8 (as of beta 5):
UIInputViewController's textDocumentProxy property:
Defined in Objective-C as follows:
#property(nonatomic, readonly) NSObject<UITextDocumentProxy> *textDocumentProxy;
and in Swift:
var textDocumentProxy: NSObject! { get }
Link to Apple' documentation:
https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy

Related

How to write a generic Swift class function to initialize classes using a closure?

I'm trying to write a generic class function for Swift classes that would allow me to initialize classes using trailing closure syntax.
I have already got it working for specific classes, like for example UILabel.
// Extension for UILabel
extension UILabel {
class func new(_ initialization: (inout UILabel) -> Void) -> UILabel {
var label = UILabel()
initialization(&label)
return label
}
}
// Initialize new UILabel using trailing closure syntax and "new" function
let label = UILabel.new {
$0.textColor = .red
}
However, I want to have this functionality for all subclasses of NSObject, so I'm trying to implement a generic version of the "new" function above. So far I have come up with this:
extension NSObject {
class func new(_ initialization: (inout Self) -> Void) -> Self {
var newSelf = Self()
initialization(&newSelf)
return newSelf
}
}
But this produces the following error: 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'NSObject'?
I am trying this in a playground with Swift 5.1 (Xcode 11 beta).
As Hamish said, maybe it's better to leave the init outside so it is more flexible. However there could be a kind of workaround for this using a protocol.
protocol Initializable {
init()
}
extension Initializable {
static func new(_ initialization: (inout Self) -> Void) -> Self {
var newSelf = Self()
initialization(&newSelf)
return newSelf
}
}
extension NSObject: Initializable {}
NSObject already have an init so it automatically conforms to Initializable.
Then write your extension on the protocol.
The only thing to be aware is that you cannot use class modifier now, as you're in a protocol.
Not sure if this can lead to problem for the NS_UNAVAILABLE modifier, I think it could crash at runtime when you use this on a class that is hiding the init.

What's the proper way to subclass a delegate?

I'm trying to learn the delegation process for Swift in Xcode 8.
I can get it working just fine, but have a question about the subclass in my delegate. Normally, in Objective-C, the subclass for this would be NSObject. I'm able to get it working with NSObject and AnyObject. I read a article about not crossing Objective-C because of performance. Does this really matter? If it's not a view or any other type of controller, what's the subclass in Swift for an object?
Is AnyObject the same as NSObject?
ViewController
import UIKit
class ViewController: UIViewController, TestDelegate {
// init the delegate
let theDelegate = TheDelegate ()
#IBOutlet weak var label1: UILabel!
#IBAction func button1(_ sender: Any) {
// tell the delegate what to do
theDelegate.run(add: 1)
}
override func viewDidLoad() {
super.viewDidLoad()
theDelegate.delegate = self
}
// the protocol
func didTest(int: Int) {
label1.text = "\(int)"
print ("Got back from delegate \(int)")
}
}
Object TestProtocol.swift
import Foundation
protocol TestDelegate: class {
func didTest(int: Int)
}
class TheDelegate: AnyObject{
weak var delegate: TestDelegate?
func run(add: Int){
let test = add + 1
delegate?.didTest(int: test)
}
}
There is no reason to subclass AnyObject, since everything is an AnyObject just by existing (like object in Java).
NSObject is the old base class in Obj-C, so if you are wanting to have your protocol or class be seen in Obj-C code it must either be marked #objc or it must subclass NSObject.
So unless you are also using Obj-C in your program, then you don't need to subclass NSObject.
Look at
Swift 3: subclassing NSObject or not?
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

Swift generics, cannot infer type T

I've tried a bunch of different things but i'm not good at generics.
How do I call a function with a variable that needs to be both a solid type like UIViewController, as well as conforming to a protocol like NavBarResponder ?
Thanks!
weak var lastNavBarResponder: UIViewController?
func reloadExtendedNavBar() {
self.updateState()
self.updateStatusBadges()
if let vc = self.lastNavBarResponder {
self.setup(viewController: vc) // Error: Generic parameter T cannot be inferred
}
}
func setup<T: UIViewController>(viewController: T) where T: NavBarResponder {
...
I suspect the error is because UIViewController doesn't naturally conform to NavBarResponder, so the compiler can't guarantee that lastNavBarResponder fits the requirements of setup.
Try changing lastNavBarResponder to be of a type that is a UIViewController that conforms to the NavBarResponder protocol.
Here is a self-contained example showing how this would be implemented.
protocol NavBarResponder {}
class NavBarResponderController: UIViewController {}
extension NavBarResponderController: NavBarResponder {}
var lastNavBarResponder: NavBarResponderController? = NavBarResponderController()
func setup<T: UIViewController>(viewController: T) where T: NavBarResponder {
print("Works")
}
func reloadExtendedNavBar() {
if let vc = lastNavBarResponder {
setup(viewController: vc)
}
}
reloadExtendedNavBar()

Swift 3 Parameter: single variable, two types

Anyone know if there's a way to have a parameter be of two types?
Such as for the function:
func email(from viewcontroller : (UIViewController, MFMailComposeViewControllerDelegate) {
}
Genericize your function's definition:
func email<T>(from viewController : T) where T: UIViewController, T: MFMailComposeViewControllerDelegate {
// ...
}
In Swift 4
func email(from viewcontroller : UIViewController & MFMailComposeViewControllerDelegate) {
}
Elegant, short and intuitive.

Reference to class that conforms to a protocol

Let's say I have a protocol that looks like this:
protocol Foo {
var bar: Bool { get set }
}
Now I have my view controller that conforms to the Foo protocol;
class FooViewController: UIViewController, Foo {
...
}
In another class I want to do something like this:
class FooClass {
var viewController: UIViewController? // this should conform to Foo protocol
func setViewController(viewController: UIViewController) {
if let fooVC = viewController as? Foo {
// viewController implements Foo protocol
self.viewController = fooVC
} else {
print("ViewController does not conform to Foo protocol")
}
}
}
In Objective-C I would have a reference that looks like: UIViewController<Foo> *vc saying that vc is an object of class UIViewController conforming to Foo protocol. Is there a Swift equivalent?
EDIT: I guess it's not possible :(
Warning: This solution may become very painful.
As a workaround, you may use protocol compositions and eventually extensions.
In your example you could be interested only in UIResponder protocol methods which UIViewController conforms to.
typealias FooController = protocol<UIResponder, Foo>
class FooClass {
var viewController: FooController?
func setViewController(viewController: FooController) {
// viewController implements UIResponder and Foo protocols
}
}
But UIViewController has properties and methods that does not conforms to any protocol. In this case, you could make a protocol containing the methods that you want and extend UIViewController.
protocol ViewEvent {
func viewWillAppear(animated: Bool)
func viewDidAppear(animated: Bool)
func viewWillDisappear(animated: Bool)
func viewDidDisappear(animated: Bool)
}
extension UIViewController: ViewEvent {}
typealias FooController = protocol<UIResponder, ViewEvent, Foo>
This is the solution:
typealias FooController = UIViewController & Foo
class FooClass {
var viewController: FooController?
func setViewController(viewController: FooController) {
self.viewController = viewController
}
}
I see what you mean.
I don't think you can declare a variable of a given class type and conform to a protocol.
However if you just need to check whether viewController does conform to the Foo protocol (and you don't really need to use the bar property declared in Foo) then you could do this.
func setViewController(viewController: UIViewController) {
if viewController is Foo {
self.viewController = viewController
} else {
print("ViewController does not conform to Foo protocol")
}
}
You can define as viewController:Foo?
Based on our comment you should used if let fooVC = viewController as? FooViewController. If you are optional binding the viewController to Foo then assigning it to a UIViewController instance will not work.