Reference to class that conforms to a protocol - swift

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.

Related

Best approach for reusable swift code UIKit

I cannot see a practical approach for reusable storyboard patterns in Xcode/Swift. I have a number of ViewControllers which have almost identical behavior and appearance. So the viewDidLoad() is always the same:
override func viewDidLoad() {
super.viewDidLoad()
labelKESumme.text = something
}
Because each ViewController has to be configured with its own class and Swift lacks multi inheritance I've tried to create an extension like this:
extension UIViewController {
func show() {
self.labelKESumme.text = something <== ... has no member
}
}
But that doesn't work due to 'has no member' errors. Also it's not possible to configure outlets between the label and the extension code. Need an approach for configure many view controllers with same content without code duplication.
You can use protocol extensions like below
// protocol
protocol Showable {
var labelKESumme: UILabel?
func show()
}
//protocol extension if you want same functionality everywhere
extension Showable {
func show() {
labelKESumme?.text = something
}
}
// just conform the the protocol
ABCViewController: Showable {
}
// just conform the the protocol
XYZViewController: Showable {
}
// In some code where you have viewController as UIViewController and you know
// that this object conforms to Showable protocol then you can do like this
guard let showable = viewController as? Showable {
return
}
showable.show()

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

How to make self.dynamicType into a typealias

Basically I want to add a typealias to UIViewController or any other default swift classes. The reasoning behind this is that I want to abstract my code so that I can access some static functions by just using this instead of self.dynamicType
extension UIViewController {
typealias this = TheClassThatSubclassedThis
}
class DetailViewController: UIViewController {
func doStuff() {
this.doStaticStuff()
}
static func doStaticStuff() {
...
}
}
I know this is possible by creating a protocol, then just implement said protocol to the class I want to implement it to, like this
protocol CanAccessStaticSelf {
typealias this
}
class DetailVC: UIViewController, CanAccessStaticSelf {
typealias this = DetailVC
}
But is there a more efficient way to do this? Like for example, by just subclassing a certain class or by extending a superclass?
Like this for example
extension UIViewController {
public static var defaultNibName: String {
return self.description().componentsSeparatedByString(".").dropFirst().joinWithSeparator(".")
}
}
class DetailVC: UIViewController, CanAccessStaticSelf {
func doSomeStuffAgain() {
// no other code just subclass and I can access self.dynamicType as just `this`
print(this.defaultNibName)
}
}
Try this instead:
protocol CanAccessStaticSelf {
typealias this = Self
}
...but what you are trying to achieve looks somewhat confusing to me ;-(
Thanks to this proposal from Erica Sadun we all might be able to use the Self keyword for that in the near future.
For instance:
class MyClass {
static func staticMethod() { ... }
func instanceMethod() {
MyClass.staticMethod()
Self.staticMethod()
}
}
it is not possible to access through this.
but you can access through "UIViewController.defaultNibName".

Swift Properties that conforms to a protocl

I have this protocol:
protocol TestProtocol {
func doSomething
}
I would like to use this protocol to ensure some properties are conforming to it like:
class MyClass {
var detailVC : UIViewController <TestProtocol>
}
like good old ObjC to ensure the detailVC conforms to TestProtocol
protocol MyViewControllerProtocol {
func protoFunc()
}
class MyClass {
var prop: MyViewControllerProtocol?
}
It's as simple as that. But if you want a pre-defined class to conform to a protocol, you then need to make an extension (but then this applies to the class as a whole) or you subclass it.
So...
As an extension to the class as a whole:
extension UIViewController: MyProtocol {
func protoFunc() {
print("do whatever")
}
}
In this case, when extended, you can just set the property as:
var myProperty: UIViewController?
As after being extended, it'll conform as required.
Or just subclass it with:
class MyConformingViewController: UIViewController, MyProtocol {
override func protoFunc() {
print("do whatever")
}
}
In this case, you just set the property as:
var myProp: MyConformingViewController?
And that'll automatically confirm to MyProtocol due to the class being set to conform to it.
You can't force a predesignated class to conform to a protocol which wasn't already designated to conform to it in the first place.
e.g. UIViewController wasn't originally set to confirm to MyOtherProtocol for example
That would defeat the object of protocols in the first place. This is why you either extend it to conform, or subclass it to conform.
So you can implement the method like following:
class detailVC : UIViewController, TestProtocol {
func doSomething() {}
}
In Swift you can't have a variable of one type and also declared as a protocol type.
What you can have is a variable that needs conform more than one protocol.
class MyClass {
var detailVC : TestProtocol
}
class MyClass {
var detailVC : protocol<TestProtocol,SecondProtocol>
}

How to declare a swift class with a property that conforms to some protocol

Is it possible for swift to have a ViewController class, initialized from xib, has a property that is also a subclass of UIViewController and conforms to some protocol?
protocol SomeProtocol {
// Some methods
}
class ViewController: UIViewController {
// contentView is initialized from xib
#IBOutlet weak var contentView: UIView!
// I'd like to declare anotherViewController both conforms to 'SomeProtocol'
// and a subclass of UIViewController
var anotherViewController: UIViewController!
...
}
When I declare ViewController as an generic class, say class ViewController<T: UIViewController, SomeProtocol>, I get an error :
"Variable in a generic class cannot be presented in Objective-C"
So how can I fulfil it if I cannot use generic class?
Please forgive me if I misunderstood your problem, but I think what you want to do is declare a new type that inherits from UIViewController and conforms to SomeProtocol, like so:
protocol SomeProtocol { }
class VCWithSomeProtocol: UIViewController, SomeProtocol {
}
class ViewController: UIViewController {
var anotherViewController: VCWithSomeProtocol!
}
So I hope I am not misunderstanding the question as well, but it sounds like you may want a multiple-inheritance object level mixin such as:
let myVC: ViewController, SomeProtocol
Unfortunately, Swift does not support this. However, there is a somewhat awkward work-around that may serve your purposes.
struct VCWithSomeProtocol {
let protocol: SomeProtocol
let viewController: UIViewController
init<T: UIViewController>(vc: T) where T: SomeProtocol {
self.protocol = vc
self.viewController = vc
}
}
Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.
For Instance:
class SomeClass {
let mySpecialViewController: VCWithSomeProtocol
init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
}
}
Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.
Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.
Hope this helps!