Swift require protocol extension - swift

If I have
protocol AppearingTextContainer {
func clipBounds() -> ()
}
extension AppearingTextContainer where Self: UIView {
func clipBounds() { self.clipsToBounds = true }
}
then a class that adopts my protocol CustomView: AppearingTextContainer is not forced by the compiler to implement clipBounds. If I remove the extension it won't compile. Is there any way to enforce CustomView to call clipBounds without having to delete the default clipBounds implementation?
Thanks

You've only provided a method that will become available to any UIView that chooses to conform to AppearingTextContainer.
So now ideally you would create a UIView subclass that conforms
class AppearingTextView: UIView, AppearingTextContainer {
// you can now call clipBounds() on this class
}
or
extension UIView: AppearingTextView { }
what I think you should consider is whether or not what you really want is a protocol extension vs. just an extension on UIView:
extension UIView {
func clipsBounds() -> Void { clipsToBounds = true }
}
if you want a protocol, I suggest going the route of:
protocol ClippingView {
var clipsToBounds: Bool { get set }
}
extension ClippingView {
func clipsBounds() -> Void { clipsToBounds = true }
}
extension UIView: ClippingView { }
let clippingViews: [ClippingView] = [......]
clippingViews.forEach { $0.clipsBounds() }

If your custom view class has its own clipBounds function, then you would have to cast your custom view to an AppearingTextContainer in order to get the AppearingTextContainer protocol extension implementation.
But keep in mind that Objective-C cannot see your protocol extension, so Cocoa will never call the protocol extension clipBounds for you; only a direct Swift call can call it.

Related

How to make protocol conformed by only required classes?

I'm trying to achieve type constraint on protocols. In my current project I have a following base controller. I examined this answer too but I don't understand why it isn't working.
class BaseViewController: UIViewController {
}
I declared two protocol based on my requirements.
protocol A: AnyObject {
func execute()
}
extension A {
func execute() {
print("Execute")
}
}
protocol B {
func confirm()
}
extension B where Self: BaseViewController & A {
func confirm() {
}
}
What I'm trying to achieve is to prevent all classes which doesn't conform protocol A and BaseViewController also can't conform protocol B also.
However, when I try to conform protocol B in another UIViewController which doesn't conform protocol A there is no error.
class AnotherVC: UIViewController {
}
extension AnotherVC: B {
func confirm() {
}
}
How can I restrict other view controllers to conform protocol B if they don't conform protocol A and inherit from BaseViewController
I am not sure if below code is what you need, do let me know if that’s what you were looking for, else I will be happy to remove my answer.
protocol A:BaseViewController {
func execute()
}
protocol B:A {
func confirm()
}
class BaseViewController: UIViewController {
}
class AnotherVC: B {
}
In above code compiler will give error saying-:
'A' requires that 'AnotherVC' inherit from ‘BaseViewController'
Once you inherit AnotherVC from BaseViewController, it will give another error saying-:
Type 'AnotherVC' does not conform to protocol ‘A'
Once you confirm the implementations errors will be resolved-:
class AnotherVC:BaseViewController, B {
func confirm() {
}
func execute() {
}
}

Swift Generics: Extending a non-generic type with a property of generic type where the generic parameter is the extended type

Problem
I have a type that takes one generic parameter that is required to inherit from UIView:
class Handler<View: UIView> {
...
}
Now, I want write a UIView extension to provide a property that returns Handler and uses Self as the generic parameter, so that in subclasses of UIView I'd always get the handler of type Handler<UIViewSubclass>:
extension UIView {
var handler: Handler<Self>? { return nil }
}
However this does not compile:
Covariant 'Self' can only appear at the top level of property type
I have also tried to define a protocol HandlerProvider first:
public protocol HandlerProvider {
associatedtype View: UIView
var handler: Handler<View>? { get }
}
(so far so good) and then extend UIView with that protocol:
extension UIView: HandlerProvider {
public typealias View = Self
public var handler: Handler<View>? { return nil }
}
But that does not compile either:
Covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'UIView'?
Question
Is there a way in Swift to use Self as a generic parameter for properties in extension?
Here is possible approach (to think with generics in a bit different direction).
Tested with Xcode 11.4 / swift 5.2
// base handling protocol
protocol Handling {
associatedtype V: UIView
var view: V { get }
init(_ view: V)
func handle()
}
// extension for base class, will be called by default for any
// UIView instance that does not have explicit extension
extension Handling where V: UIView {
func handle() {
print(">> base: \(self.view)")
}
}
// extension for specific view (any more you wish)
extension Handling where V: UIImageView {
func handle() {
print(">> image: \(self.view)")
}
}
// concrete implementer
class Handler<V: UIView>: Handling {
let view: V
required init(_ view: V) {
self.view = view
}
}
// testing function
func fooBar() {
// handlers created in place of handling where type of
// handling view is know, so corresponding handle function
// is used
Handler(UIView()).handle()
Handler(UIImageView()).handle()
}
Output:

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

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".

Argument of `#selector` does not refer to an initializer or method

I'm trying to perform a protocol extension method in the background:
performSelectorInBackground(#selector(retrieveCategories()), withObject: nil)
However I get the below error message:
Argument of `#selector` does not refer to an initializer or method
Here is my protocol declaration:
#objc protocol DataRetrievalOperations {
optional func retrieveCategories()
...
}
And my extension:
extension DataRetrievalOperations {
func retrieveCategories() {
...
}
}
How can I achieve this?
Try this:
#selector(DataRetrievalOperations.retrieveCategories)
With omitting class (or protocol) name in #selector(...) notation, Swift assumes the enclosing class, which may be a ViewController, I guess.
One more issue:
It seems Swift cannot implement #objc protocol methods with default implementation in protocol extension.
(I think I have heard something about this, but I couldn't find any articles for now.)
You may need to implement it in your own class's extension or find another way.
extension CategoriesViewController {
func retrieveCategories() {
//...
}
}
I need to add that this will solve the first issue and #selector(retrieveCategories) will work.
You can't add an #Objc method in a Protocol Extension. You need to extend the Class which inherits NSObject and that Protocol and add the objc function there like so:
#objc protocol DataRetrievalOperations {
optional func retrieveCategories()
}
class aClass: NSObject, DataRetrievalOperations {
func method() {
performSelectorInBackground(#selector(retrieveCategories), withObject: nil)
}
}
extension aClass {
#objc func retrieveCategories(){
}
}
This will work.